Merge "[CameraX] Add public constructor in ResolutionInfo for better testability" into androidx-main
diff --git a/OWNERS b/OWNERS
index 13e8cd6..e6c536a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -26,6 +26,7 @@
 per-file *settings.gradle = set noparent
 per-file *settings.gradle = [email protected], [email protected]
 per-file *libraryversions.toml = [email protected]
+per-file *libraryversions.toml = [email protected], [email protected], [email protected]
 
 # Copybara can self-approve CLs within synced docs.
 per-file docs/** = [email protected]
\ No newline at end of file
diff --git a/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt b/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
index 1174984..c5a9c26528 100644
--- a/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
+++ b/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
@@ -17,10 +17,8 @@
 package androidx.activity.compose.samples
 
 import androidx.activity.compose.BackHandler
-import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
 import androidx.annotation.Sampled
-import androidx.compose.material.Button
-import androidx.compose.material.Text
+import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -30,12 +28,14 @@
 @Sampled
 @Composable
 fun BackHandler() {
-    var backPressedCount by remember { mutableStateOf(0) }
-    BackHandler { backPressedCount++ }
+    var text by remember { mutableStateOf("") }
 
-    val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+    TextField(
+        value = text,
+        onValueChange = { text = it }
+    )
 
-    Button(onClick = { dispatcher.onBackPressed() }) {
-        Text("Press Back count $backPressedCount")
+    BackHandler(text.isNotEmpty()) {
+        // handle back event
     }
 }
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 021c371..5362c618 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
@@ -77,7 +77,7 @@
     ) {
         private var id: String? = null
         private var property: PropertyT? = null
-        private var capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>? = null
+        private var executionCallback: ExecutionCallback<ArgumentsT, OutputT>? = null
         private var sessionFactory: ExecutionSessionFactory<ExecutionSessionT>? = null
 
         /**
@@ -111,39 +111,40 @@
         }
 
         /**
-         * Sets the CapabilityExecutor for this capability.
+         * Sets the ExecutionCallback for this capability.
          *
-         * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
-         * nullify the other.
+         * [setExecutionSessionFactory] and [setExecutionCallback] are mutually exclusive, so
+         * calling one will nullify the other.
          *
-         * This method accepts a coroutine-based CapabilityExecutor instance. There is also an overload
-         * which accepts the CapabilityExecutorAsync instead.
+         * This method accepts a coroutine-based ExecutionCallback instance. There is also an
+         * overload which accepts the ExecutionCallbackAsync instead.
          */
-        fun setExecutor(capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>) =
+        fun setExecutionCallback(executionCallback: ExecutionCallback<ArgumentsT, OutputT>) =
             asBuilder().apply {
-                this.capabilityExecutor = capabilityExecutor
+                this.executionCallback = executionCallback
             }
 
         /**
-         * Sets the CapabilityExecutorAsync for this capability.
+         * Sets the ExecutionCallbackAsync for this capability.
          *
-         * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
-         * nullify the other.
+         * setExecutionSessionFactory and setExecutionCallback are mutually exclusive, so calling
+         * one will nullify the other.
          *
-         * This method accepts the CapabilityExecutorAsync interface which returns a ListenableFuture.
+         * This method accepts the ExecutionCallbackAsync interface which returns a
+         * []ListenableFuture].
          */
-        fun setExecutor(
-            capabilityExecutorAsync: CapabilityExecutorAsync<ArgumentsT, OutputT>
+        fun setExecutionCallback(
+            executionCallbackAsync: ExecutionCallbackAsync<ArgumentsT, OutputT>
         ) = asBuilder().apply {
-            this.capabilityExecutor = capabilityExecutorAsync.toCapabilityExecutor()
+            this.executionCallback = executionCallbackAsync.toExecutionCallback()
         }
 
         /**
          * Sets the SessionBuilder instance which is used to create Session instaces for this
          * capability.
          *
-         * [setExecutionSessionFactory] and [setExecutor] are mutually exclusive, so calling one
-         * will nullify the other.
+         * [setExecutionSessionFactory] and [setExecutionCallback] are mutually exclusive, so
+         * calling one will nullify the other.
          */
         protected open fun setExecutionSessionFactory(
             sessionFactory: ExecutionSessionFactory<ExecutionSessionT>
@@ -155,16 +156,17 @@
         open fun build(): Capability {
             val checkedId = requireNotNull(id) { "setId must be called before build" }
             val checkedProperty = requireNotNull(property) { "property must not be null." }
-            if (capabilityExecutor != null) {
+            if (executionCallback != null) {
                 return SingleTurnCapabilityImpl(
                     checkedId,
                     actionSpec,
                     checkedProperty,
-                    capabilityExecutor!!
+                    executionCallback!!
                 )
             } else {
                 val checkedSessionFactory = requireNotNull(sessionFactory) {
-                    "either setExecutor or setExecutionSessionFactory must be called before build"
+                    "either setExecutionCallback or setExecutionSessionFactory" +
+                        " must be called before build"
                 }
                 return TaskCapabilityImpl(
                     checkedId,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
similarity index 76%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
index 01e9aae..87ff079 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
@@ -23,9 +23,9 @@
  * An interface of executing the action.
  *
  * Actions are executed asynchronously using Kotlin coroutines.
- * For a Future-based solution, see CapabilityExecutorAsync.
+ * For a Future-based solution, see ExecutionCallbackAsync.
  */
-fun interface CapabilityExecutor<ArgumentsT, OutputT> {
+fun interface ExecutionCallback<ArgumentsT, OutputT> {
     @get:RestrictTo(RestrictTo.Scope.LIBRARY)
     val uiHandle: Any
         get() = this
@@ -40,9 +40,9 @@
 }
 
 internal fun <ArgumentsT, OutputT>
-    CapabilityExecutorAsync<ArgumentsT, OutputT>.toCapabilityExecutor():
-    CapabilityExecutor<ArgumentsT, OutputT> = object : CapabilityExecutor<ArgumentsT, OutputT> {
-    override val uiHandle = this@toCapabilityExecutor
+    ExecutionCallbackAsync<ArgumentsT, OutputT>.toExecutionCallback():
+    ExecutionCallback<ArgumentsT, OutputT> = object : ExecutionCallback<ArgumentsT, OutputT> {
+    override val uiHandle = this@toExecutionCallback
     override suspend fun onExecute(arguments: ArgumentsT): ExecutionResult<OutputT> =
-        [email protected](arguments).await()
+        [email protected](arguments).await()
 }
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
similarity index 94%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
index 73d04fd..239f6a4 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
@@ -19,7 +19,7 @@
 import com.google.common.util.concurrent.ListenableFuture
 
 /** An ListenableFuture-based interface of executing an action. */
-fun interface CapabilityExecutorAsync<ArgumentsT, OutputT> {
+fun interface ExecutionCallbackAsync<ArgumentsT, OutputT> {
     /**
      * Calls to execute the action.
      *
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
index 632ce37..e2ced1d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
@@ -62,7 +62,7 @@
 
     /**
      * The developer-provided external object (either a BaseExecutionSession instance or an
-     * CapabilityExecutor instance).
+     * ExecutionCallback instance).
      */
     val uiHandle: Any
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
index 4ff60e0..050d370 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl
 
 import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
 import androidx.appactions.interaction.capabilities.core.Capability
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
@@ -35,11 +35,11 @@
     id: String,
     val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
     val property: PropertyT,
-    val capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>,
+    val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
 ) : Capability(id) {
     private val mutex = Mutex()
 
-    override val appAction: AppAction =
+    override val appAction: AppAction get() =
         actionSpec.convertPropertyToProto(property).toBuilder()
             .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
             .setIdentifier(id)
@@ -52,7 +52,7 @@
         return SingleTurnCapabilitySession(
             sessionId,
             actionSpec,
-            capabilityExecutor,
+            executionCallback,
             mutex,
         )
     }
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 0bb184a..015147a 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
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl
 
 import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+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.proto.AppActionsContext.AppDialogState
@@ -42,7 +42,7 @@
     >(
     override val sessionId: String,
     private val actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
-    private val capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>,
+    private val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
     private val mutex: Mutex,
     private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
 ) : CapabilitySession {
@@ -51,7 +51,7 @@
     override val state: AppDialogState? = null
     override val isActive: Boolean get() = isActiveAtomic.get()
 
-    override val uiHandle: Any = capabilityExecutor.uiHandle
+    override val uiHandle: Any = executionCallback.uiHandle
 
     override fun destroy() {}
 
@@ -75,7 +75,7 @@
             try {
                 mutex.lock(owner = this@SingleTurnCapabilitySession)
                 UiHandleRegistry.registerUiHandle(uiHandle, sessionId)
-                val output = capabilityExecutor.onExecute(arguments)
+                val output = executionCallback.onExecute(arguments)
                 callback.onSuccess(convertToFulfillmentResponse(output))
             } catch (t: Throwable) {
                 callback.onError(ErrorStatusInternal.CANCELLED)
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 34f1ddc..075d36f3 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
@@ -42,6 +42,7 @@
 import java.time.LocalTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.util.Optional;
 
@@ -213,8 +214,9 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(LocalDate value) {
-                    // TODO(b/275456249): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder()
+                            .setStringValue(value.format(DateTimeFormatter.ISO_LOCAL_DATE))
+                            .build();
                 }
 
                 @Override
@@ -237,8 +239,9 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(LocalTime value) {
-                    // TODO(b/275456249)): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder()
+                            .setStringValue(value.format(DateTimeFormatter.ISO_LOCAL_TIME))
+                            .build();
                 }
 
                 @Override
@@ -261,8 +264,7 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(ZoneId value) {
-                    // TODO(b/275456249)): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder().setStringValue(value.getId()).build();
                 }
 
                 @Override
@@ -285,8 +287,9 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(ZonedDateTime value) {
-                    // TODO(b/275456249)): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder()
+                            .setStringValue(value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME))
+                            .build();
                 }
 
                 @Override
@@ -310,8 +313,7 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(Duration value) {
-                    // TODO(b/275456249)): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder().setStringValue(value.toString()).build();
                 }
 
                 @Override
@@ -337,8 +339,9 @@
                 @NonNull
                 @Override
                 public ParamValue toParamValue(Call.CanonicalValue.CallFormat value) {
-                    // TODO(b/275456249)): Implement backwards conversion.
-                    return ParamValue.getDefaultInstance();
+                    return ParamValue.newBuilder()
+                        .setStringValue(value.getTextValue())
+                        .build();
                 }
 
                 @Override
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 fade42a..3c51e47 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
@@ -51,7 +51,7 @@
     private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>
 ) : Capability(id) {
 
-    override val appAction: AppAction =
+    override val appAction: AppAction get() =
         actionSpec
             .convertPropertyToProto(property)
             .toBuilder()
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 3779b5c..07ae86c 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
@@ -18,9 +18,9 @@
 
 import androidx.annotation.GuardedBy
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
-import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.ArgumentsWrapper
 import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
+import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
 import androidx.appactions.interaction.capabilities.core.impl.TouchEventCallback
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
@@ -30,6 +30,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
 
 internal class TaskCapabilitySession<
@@ -58,6 +59,7 @@
     override fun destroy() {
         // TODO(b/270751989): cancel current processing request immediately
         this.sessionOrchestrator.terminate()
+        scope.cancel()
     }
 
     override val uiHandle: Any = externalSession
@@ -71,16 +73,17 @@
             ArgumentsT,
             OutputT,
             ConfirmationT,
-        > =
+            > =
         TaskOrchestrator(
             sessionId,
             actionSpec,
             appAction,
             taskHandler,
             externalSession,
+            scope,
         )
-
-    @GuardedBy("requestLock") private var pendingAssistantRequest: AssistantUpdateRequest? = null
+    @GuardedBy("requestLock")
+    private var pendingAssistantRequest: AssistantUpdateRequest? = null
     @GuardedBy("requestLock") private var pendingTouchEventRequest: TouchEventUpdateRequest? = null
 
     override fun execute(argumentsWrapper: ArgumentsWrapper, callback: CallbackInternal) {
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 8d00b16..b0e1396 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
@@ -27,12 +27,12 @@
 import androidx.appactions.interaction.capabilities.core.impl.UiHandleRegistry
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
 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.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.proto.AppActionsContext
 import androidx.appactions.interaction.proto.CurrentValue
 import androidx.appactions.interaction.proto.FulfillmentRequest
@@ -42,7 +42,8 @@
 import java.util.concurrent.locks.ReentrantReadWriteLock
 import kotlin.concurrent.read
 import kotlin.concurrent.write
-import kotlin.jvm.Throws
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.isActive
 
 /**
  * TaskOrchestrator is responsible for holding session state, and processing assistant / manual
@@ -59,6 +60,7 @@
     private val appAction: AppActionsContext.AppAction,
     private val taskHandler: TaskHandler<ConfirmationT>,
     private val externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
+    private val scope: CoroutineScope,
 ) {
     /** This enum describes the current status of the TaskOrchestrator. */
     internal enum class Status {
@@ -348,6 +350,9 @@
     ) {
         var currentResult = SlotProcessingResult(true, emptyList())
         for ((name, fulfillmentValues) in fulfillmentValuesMap) {
+            if (!scope.isActive) {
+                break
+            }
             currentResult =
                 maybeProcessSlotAndUpdateCurrentValues(currentResult, name, fulfillmentValues)
         }
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 a8b66c3..b6d8e63 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
@@ -17,9 +17,9 @@
 package androidx.appactions.interaction.capabilities.core.impl
 
 import android.util.SizeF
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutorAsync
-import androidx.appactions.interaction.capabilities.core.toCapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
+import androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync
+import androidx.appactions.interaction.capabilities.core.toExecutionCallback
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
@@ -35,10 +35,13 @@
 import androidx.appactions.interaction.capabilities.core.testing.spec.Arguments
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output
 import androidx.appactions.interaction.capabilities.core.testing.spec.Properties
+import androidx.appactions.interaction.proto.AppActionsContext.AppAction
+import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue
 import androidx.appactions.interaction.proto.ParamValue
+import androidx.appactions.interaction.proto.TaskInfo
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.runBlocking
@@ -54,9 +57,66 @@
     private val fakeSessionId = "fakeSessionId"
 
     @Test
+    fun appAction_computedProperty() {
+        val mutableEntityList = mutableListOf<Entity>()
+        val capability = SingleTurnCapabilityImpl(
+            id = "capabilityId",
+            actionSpec = ACTION_SPEC,
+            property = Properties.newBuilder()
+                .setRequiredEntityField(
+                    Property.Builder<Entity>().setPossibleValueSupplier(
+                        mutableEntityList::toList
+                    ).build()
+                )
+                .build(),
+            executionCallback = ExecutionCallback<Arguments, Output> {
+                ExecutionResult.Builder<Output>().build()
+            }
+        )
+        mutableEntityList.add(Entity.Builder().setName("entity1").build())
+
+        assertThat(capability.appAction).isEqualTo(
+            AppAction.newBuilder()
+                .setIdentifier("capabilityId")
+                .setName("actions.intent.TEST")
+                .addParams(
+                    IntentParameter.newBuilder()
+                        .setName("requiredEntity")
+                        .addPossibleEntities(
+                            androidx.appactions.interaction.proto.Entity.newBuilder()
+                                .setName("entity1")
+                        )
+                )
+                .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
+                .build()
+        )
+
+        mutableEntityList.add(Entity.Builder().setName("entity2").build())
+        assertThat(capability.appAction).isEqualTo(
+            AppAction.newBuilder()
+                .setIdentifier("capabilityId")
+                .setName("actions.intent.TEST")
+                .addParams(
+                    IntentParameter.newBuilder()
+                        .setName("requiredEntity")
+                        .addPossibleEntities(
+                            androidx.appactions.interaction.proto.Entity.newBuilder()
+                                .setName("entity1")
+                        )
+                        .addPossibleEntities(
+                            androidx.appactions.interaction.proto.Entity.newBuilder()
+                                .setName("entity2")
+                        )
+                )
+                .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
+                .build()
+        )
+    }
+
+    @Test
     fun oneShotCapability_successWithOutput() {
-        val capabilityExecutor =
-            CapabilityExecutor<Arguments, Output> {
+        val executionCallback =
+            ExecutionCallback<Arguments, Output> {
                 ExecutionResult.Builder<Output>()
                     .setOutput(
                         Output.builder().setOptionalStringField("stringOutput").build()
@@ -74,7 +134,7 @@
                     )
                     .setOptionalStringField(Property.prohibited())
                     .build(),
-                capabilityExecutor = capabilityExecutor
+                executionCallback = executionCallback
             )
 
         val capabilitySession = capability.createSession(fakeSessionId, hostProperties)
@@ -119,8 +179,8 @@
 
     @Test
     fun oneShotCapability_failure() {
-        val capabilityExecutor =
-            CapabilityExecutor<Arguments, Output> { throw IllegalStateException("") }
+        val executionCallback =
+            ExecutionCallback<Arguments, Output> { throw IllegalStateException("") }
         val capability =
             SingleTurnCapabilityImpl(
                 id = "capabilityId",
@@ -132,7 +192,7 @@
                     )
                     .setOptionalStringField(Property.prohibited())
                     .build(),
-                capabilityExecutor = capabilityExecutor
+                executionCallback = executionCallback
             )
 
         val capabilitySession = capability.createSession(fakeSessionId, hostProperties)
@@ -153,9 +213,9 @@
     }
 
     @Test
-    fun oneShotSession_uiHandle_withCapabilityExecutor() {
-        val capabilityExecutor =
-            CapabilityExecutor<Arguments, Output> { ExecutionResult.Builder<Output>().build() }
+    fun oneShotSession_uiHandle_withExecutionCallback() {
+        val executionCallback =
+            ExecutionCallback<Arguments, Output> { ExecutionResult.Builder<Output>().build() }
         val capability =
             SingleTurnCapabilityImpl(
                 id = "capabilityId",
@@ -166,16 +226,16 @@
                         Property.Builder<Entity>().build()
                     )
                     .build(),
-                capabilityExecutor = capabilityExecutor
+                executionCallback = executionCallback
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
-        assertThat(session.uiHandle).isSameInstanceAs(capabilityExecutor)
+        assertThat(session.uiHandle).isSameInstanceAs(executionCallback)
     }
 
     @Test
-    fun oneShotSession_uiHandle_withCapabilityExecutorAsync() {
-        val capabilityExecutorAsync =
-            CapabilityExecutorAsync<Arguments, Output> {
+    fun oneShotSession_uiHandle_withExecutionCallbackAsync() {
+        val executionCallbackAsync =
+            ExecutionCallbackAsync<Arguments, Output> {
                 Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
             }
         val capability =
@@ -188,10 +248,10 @@
                         Property.Builder<Entity>().build()
                     )
                     .build(),
-                capabilityExecutor = capabilityExecutorAsync.toCapabilityExecutor()
+                executionCallback = executionCallbackAsync.toExecutionCallback()
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
-        assertThat(session.uiHandle).isSameInstanceAs(capabilityExecutorAsync)
+        assertThat(session.uiHandle).isSameInstanceAs(executionCallbackAsync)
     }
 
     @Test
@@ -199,7 +259,7 @@
         val executionResultChannel = Channel<ExecutionResult<Output>>()
         val argumentChannel = Channel<Arguments>()
 
-        val capabilityExecutor = CapabilityExecutor<Arguments, Output> {
+        val executionCallback = ExecutionCallback<Arguments, Output> {
             argumentChannel.send(it)
             executionResultChannel.receive()
         }
@@ -209,7 +269,7 @@
             property = Properties.newBuilder().setRequiredEntityField(
                 Property.Builder<Entity>().build()
             ).build(),
-            capabilityExecutor = capabilityExecutor
+            executionCallback = executionCallback
         )
         val session1 = capability.createSession("session1", hostProperties)
         val session2 = capability.createSession("session2", hostProperties)
@@ -236,7 +296,7 @@
             callbackInternal2
         )
 
-        // verify CapabilityExecutor receives 1st request.
+        // verify ExecutionCallback receives 1st request.
         assertThat(argumentChannel.receive()).isEqualTo(
             Arguments.newBuilder().setOptionalStringField("string value 1").build()
         )
@@ -266,6 +326,13 @@
                 .setDescriptor(Properties::class.java)
                 .setArguments(Arguments::class.java, Arguments::newBuilder)
                 .setOutput(Output::class.java)
+                .bindParameter(
+                    "requiredEntity",
+                    Properties::requiredEntityField,
+                    Arguments.Builder::setRequiredEntityField,
+                    TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
+                    TypeConverters.ENTITY_ENTITY_CONVERTER
+                )
                 .bindOptionalParameter(
                     "optionalString",
                     Properties::optionalStringField,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
index 074820df..3deb2a0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
@@ -490,16 +490,13 @@
     }
 
     @Test
-    public void toLocalDate_success() throws Exception {
-        List<ParamValue> input =
-                Collections.singletonList(
-                        ParamValue.newBuilder().setStringValue("2018-06-17").build());
+    public void localDate_success() throws Exception {
+        ParamValueConverter<LocalDate> converter = TypeConverters.LOCAL_DATE_PARAM_VALUE_CONVERTER;
+        ParamValue paramValue = ParamValue.newBuilder().setStringValue("2018-06-17").build();
+        LocalDate localDate = LocalDate.of(2018, 6, 17);
 
-        assertThat(
-                SlotTypeConverter.ofSingular(
-                                TypeConverters.LOCAL_DATE_PARAM_VALUE_CONVERTER)
-                        .convert(input))
-                .isEqualTo(LocalDate.of(2018, 6, 17));
+        assertThat(converter.fromParamValue(paramValue)).isEqualTo(localDate);
+        assertThat(converter.toParamValue(localDate)).isEqualTo(paramValue);
     }
 
     @Test
@@ -537,16 +534,13 @@
     }
 
     @Test
-    public void toLocalTime_success() throws Exception {
-        List<ParamValue> input =
-                Collections.singletonList(
-                        ParamValue.newBuilder().setStringValue("15:10:05").build());
+    public void localTime_success() throws Exception {
+        ParamValueConverter<LocalTime> converter = TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER;
+        ParamValue paramValue = ParamValue.newBuilder().setStringValue("15:10:05").build();
+        LocalTime localTime = LocalTime.of(15, 10, 5);
 
-        assertThat(
-                SlotTypeConverter.ofSingular(
-                                TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER)
-                        .convert(input))
-                .isEqualTo(LocalTime.of(15, 10, 5));
+        assertThat(converter.fromParamValue(paramValue)).isEqualTo(localTime);
+        assertThat(converter.toParamValue(localTime)).isEqualTo(paramValue);
     }
 
     @Test
@@ -583,15 +577,13 @@
     }
 
     @Test
-    public void toZoneId_success() throws Exception {
-        List<ParamValue> input =
-                Collections.singletonList(
-                        ParamValue.newBuilder().setStringValue("America/New_York").build());
+    public void zoneId_success() throws Exception {
+        ParamValueConverter<ZoneId> converter = TypeConverters.ZONE_ID_PARAM_VALUE_CONVERTER;
+        ParamValue paramValue = ParamValue.newBuilder().setStringValue("America/New_York").build();
+        ZoneId zoneId = ZoneId.of("America/New_York");
 
-        assertThat(
-                SlotTypeConverter.ofSingular(TypeConverters.ZONE_ID_PARAM_VALUE_CONVERTER)
-                        .convert(input))
-                .isEqualTo(ZoneId.of("America/New_York"));
+        assertThat(converter.fromParamValue(paramValue)).isEqualTo(zoneId);
+        assertThat(converter.toParamValue(zoneId)).isEqualTo(paramValue);
     }
 
     @Test
@@ -627,16 +619,15 @@
     }
 
     @Test
-    public void toZonedDateTime_fromList() throws Exception {
-        List<ParamValue> input =
-                Collections.singletonList(
-                        ParamValue.newBuilder().setStringValue("2018-06-17T15:10:05Z").build());
+    public void zonedDateTime_success() throws Exception {
+        ParamValueConverter<ZonedDateTime> converter =
+                TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER;
+        ParamValue paramValue =
+                ParamValue.newBuilder().setStringValue("2018-06-17T15:10:05Z").build();
+        ZonedDateTime zonedDateTime = ZonedDateTime.of(2018, 6, 17, 15, 10, 5, 0, ZoneOffset.UTC);
 
-        assertThat(
-                SlotTypeConverter.ofSingular(
-                                TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER)
-                        .convert(input))
-                .isEqualTo(ZonedDateTime.of(2018, 6, 17, 15, 10, 5, 0, ZoneOffset.UTC));
+        assertThat(converter.fromParamValue(paramValue)).isEqualTo(zonedDateTime);
+        assertThat(converter.toParamValue(zonedDateTime)).isEqualTo(paramValue);
     }
 
     @Test
@@ -678,15 +669,14 @@
     }
 
     @Test
-    public void toDuration_success() throws Exception {
-        List<ParamValue> input =
-                Collections.singletonList(ParamValue.newBuilder().setStringValue("PT5M").build());
+    public void duration_success() throws Exception {
+        ParamValueConverter<Duration> converter = TypeConverters.DURATION_PARAM_VALUE_CONVERTER;
+        ParamValue paramValue =
+                ParamValue.newBuilder().setStringValue("PT5M").build();
+        Duration duration = Duration.ofMinutes(5);
 
-        Duration convertedDuration =
-                SlotTypeConverter.ofSingular(TypeConverters.DURATION_PARAM_VALUE_CONVERTER)
-                        .convert(input);
-
-        assertThat(convertedDuration).isEqualTo(Duration.ofMinutes(5));
+        assertThat(converter.fromParamValue(paramValue)).isEqualTo(duration);
+        assertThat(converter.toParamValue(duration)).isEqualTo(paramValue);
     }
 
     @Test
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 8c6546b..d4c4273 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
@@ -103,7 +103,7 @@
     private val fakeSessionId = "fakeSessionId"
 
     @Test
-    fun getAppAction_smokeTest() {
+    fun appAction_smokeTest() {
         assertThat(capability.appAction)
             .isEqualTo(
                 AppAction.newBuilder()
@@ -120,6 +120,68 @@
     }
 
     @Test
+    fun appAction_computedProperty() {
+        val mutableEntityList = mutableListOf<
+            androidx.appactions.interaction.capabilities.core.properties.Entity
+        >()
+        val capability = createCapability<EmptyTaskUpdater>(
+            Properties.newBuilder()
+                .setRequiredEntityField(
+                    Property.Builder<
+                        androidx.appactions.interaction.capabilities.core.properties.Entity
+                    >().setPossibleValueSupplier(
+                        mutableEntityList::toList
+                    ).build()
+                )
+                .build(),
+            sessionFactory =
+            {
+                object : ExecutionSession {
+                    override fun onExecuteAsync(arguments: Arguments) =
+                        Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
+                }
+            },
+            sessionBridge = { TaskHandler.Builder<Confirmation>().build() },
+            sessionUpdaterSupplier = ::EmptyTaskUpdater,
+        )
+        mutableEntityList.add(
+            androidx.appactions.interaction.capabilities.core.properties.Entity.Builder()
+                .setName("entity1").build()
+        )
+
+        assertThat(capability.appAction).isEqualTo(
+            AppAction.newBuilder()
+                .setIdentifier("id")
+                .setName("actions.intent.TEST")
+                .addParams(
+                    IntentParameter.newBuilder()
+                        .setName("required")
+                        .addPossibleEntities(Entity.newBuilder().setName("entity1"))
+                )
+                .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(true))
+                .build()
+        )
+
+        mutableEntityList.add(
+            androidx.appactions.interaction.capabilities.core.properties.Entity.Builder()
+                .setName("entity2").build()
+        )
+        assertThat(capability.appAction).isEqualTo(
+            AppAction.newBuilder()
+                .setIdentifier("id")
+                .setName("actions.intent.TEST")
+                .addParams(
+                    IntentParameter.newBuilder()
+                        .setName("required")
+                        .addPossibleEntities(Entity.newBuilder().setName("entity1"))
+                        .addPossibleEntities(Entity.newBuilder().setName("entity2"))
+                )
+                .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(true))
+                .build()
+        )
+    }
+
+    @Test
     fun capabilitySession_getUiHandle() {
         val externalSession = object : ExecutionSession {}
         val capability =
diff --git a/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt b/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
index 46494c3..39c7d77 100644
--- a/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
+++ b/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
@@ -16,7 +16,7 @@
 
 package androidx.appactions.interaction.capabilities.testing.internal
 
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutorAsync
+import androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
 import kotlinx.coroutines.Deferred
@@ -30,9 +30,9 @@
     // use this timeout for waiting an arbitrary period of time.
     const val BLOCKING_TIMEOUT = 300L
 
-    fun <ArgumentsT, OutputT> createFakeCapabilityExecutor():
-        CapabilityExecutorAsync<ArgumentsT, OutputT> {
-        return CapabilityExecutorAsync { _: ArgumentsT ->
+    fun <ArgumentsT, OutputT> createFakeExecutionCallback():
+        ExecutionCallbackAsync<ArgumentsT, OutputT> {
+        return ExecutionCallbackAsync { _: ArgumentsT ->
             Futures.immediateFuture(
                 ExecutionResult.Builder<OutputT>().build()
             )
diff --git a/appactions/interaction/interaction-service-proto/build.gradle b/appactions/interaction/interaction-service-proto/build.gradle
index 38a00154..884a1aa 100644
--- a/appactions/interaction/interaction-service-proto/build.gradle
+++ b/appactions/interaction/interaction-service-proto/build.gradle
@@ -47,6 +47,10 @@
     compileOnly("androidx.annotation:annotation:1.1.0")
 }
 
+jar {
+    exclude "**/*.proto"
+}
+
 protobuf {
     protoc {
         artifact = libs.protobufCompiler.get()
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
index 8c7a81f..1774fa6 100644
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
@@ -18,7 +18,7 @@
 package androidx.appactions.interaction.service
 
 import androidx.annotation.GuardedBy
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.UiHandleRegistry
 import androidx.appactions.interaction.service.UiSessions.removeUiCache
@@ -70,8 +70,8 @@
         UiHandleRegistry.getSessionIdFromUiHandle(this)!!
     ).updateUiInternal(uiResponse)
 
-/** Return a UI associated with this [CapabilityExecutor]. */
-fun CapabilityExecutor<*, *>.updateUi(uiResponse: UiResponse) =
+/** Return a UI associated with this [ExecutionCallback]. */
+fun ExecutionCallback<*, *>.updateUi(uiResponse: UiResponse) =
     UiSessions.getOrCreateUiCache(
         UiHandleRegistry.getSessionIdFromUiHandle(this)!!
     ).updateUiInternal(uiResponse)
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
index e9e60c5..d72a575 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import android.util.SizeF
 import android.widget.RemoteViews
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
@@ -252,11 +252,11 @@
     }
 
     @Test
-    fun capabilityExecutor_hasUpdateUiExtension() {
+    fun executionCallback_hasUpdateUiExtension() {
         assertThat(UiSessions.getUiCacheOrNull(sessionId)).isNull()
         val oneShotCapability = FakeCapability.CapabilityBuilder().setId(
             "oneShotCapability",
-        ).setExecutor(object : CapabilityExecutor<Arguments, Output> {
+        ).setExecutionCallback(object : ExecutionCallback<Arguments, Output> {
             override suspend fun onExecute(arguments: Arguments): ExecutionResult<Output> {
                 this.updateUi(remoteViewsUiResponse)
                 return ExecutionResult.Builder<Output>().build()
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
index bfb32b8..b48c66c 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
@@ -18,7 +18,6 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -56,6 +55,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringRes;
 import androidx.annotation.StyleRes;
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.R;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.content.res.AppCompatResources;
@@ -1607,7 +1607,7 @@
      * Returns the navigation button view.
      *
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @Nullable
     View getNavButtonView() {
         return mNavButtonView;
@@ -2418,7 +2418,7 @@
 
     /**
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     @Nullable
     final TextView getTitleTextView() {
         return mTitleTextView;
@@ -2426,7 +2426,7 @@
 
     /**
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     @Nullable
     final TextView getSubtitleTextView() {
         return mSubtitleTextView;
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
index 8b9c181..81151eb 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
@@ -19,6 +19,7 @@
 import android.os.Bundle
 import android.util.Log
 import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
 import androidx.test.platform.app.InstrumentationRegistry
 
 /**
@@ -26,13 +27,13 @@
  *
  * @hide
  */
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
 public var argumentSource: Bundle? = null
 
 /**
  * Allows tests to override profiler
  */
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
 internal var profilerOverride: Profiler? = null
 
 /**
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
index 9e372d7..7028319 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
@@ -20,7 +20,7 @@
 import android.os.Debug
 import android.util.Log
 import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
 import androidx.benchmark.BenchmarkState.Companion.TAG
 import androidx.benchmark.Outputs.dateToFileName
 import androidx.benchmark.simpleperf.ProfileSession
@@ -132,7 +132,7 @@
 }
 
 internal object StackSamplingLegacy : Profiler() {
-    @get:RestrictTo(RestrictTo.Scope.TESTS)
+    @get:VisibleForTesting
     var isRunning = false
 
     override fun start(traceUniqueName: String): ResultFile {
diff --git a/benchmark/benchmark-macro/lint-baseline.xml b/benchmark/benchmark-macro/lint-baseline.xml
index c22cb1c..fb8dd8f 100644
--- a/benchmark/benchmark-macro/lint-baseline.xml
+++ b/benchmark/benchmark-macro/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.1.0-alpha11" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-alpha11)" variant="all" version="8.1.0-alpha11">
 
     <issue
         id="BanThreadSleep"
@@ -112,15 +112,6 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="            Thread.sleep(5)"
-        errorLine2="                   ~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
         errorLine1="                Thread.sleep(500)"
         errorLine2="                       ~~~~~">
         <location
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
index 66ed720..6dfc931 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
@@ -20,6 +20,7 @@
 import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameDurationCpuNs
 import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameDurationUiNs
 import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameOverrunNs
+import androidx.benchmark.macro.perfetto.FrameTimingQuery.getFrameSubMetrics
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -40,11 +41,11 @@
         val frameSubMetrics = PerfettoTraceProcessor.runSingleSessionServer(
             traceFile.absolutePath
         ) {
-            FrameTimingQuery.getFrameSubMetrics(
+            FrameTimingQuery.getFrameData(
                 session = this,
                 captureApiLevel = 28,
                 packageName = "androidx.benchmark.integration.macrobenchmark.target"
-            )
+            ).getFrameSubMetrics(captureApiLevel = 28)
         }
 
         assertEquals(
@@ -72,11 +73,11 @@
         val frameSubMetrics = PerfettoTraceProcessor.runSingleSessionServer(
             traceFile.absolutePath
         ) {
-            FrameTimingQuery.getFrameSubMetrics(
+            FrameTimingQuery.getFrameData(
                 session = this,
                 captureApiLevel = 31,
                 packageName = "androidx.benchmark.integration.macrobenchmark.target"
-            )
+            ).getFrameSubMetrics(captureApiLevel = 31)
         }
 
         assertEquals(
@@ -95,4 +96,61 @@
             message = "Expect same number of frames for each metric"
         )
     }
+
+    /**
+     * This validates that we're able to see all frames, even if expected/actual frame IDs don't
+     * match UI thread and Renderthread (b/279088460)
+     */
+    @MediumTest
+    @Test
+    fun fixedTrace33_mismatchExpectedActualFrameIds() {
+        assumeTrue(isAbiSupported())
+        val traceFile =
+            createTempFileFromAsset("api33_motionlayout_messagejson", ".perfetto-trace")
+
+        val frameData = PerfettoTraceProcessor.runSingleSessionServer(
+            traceFile.absolutePath
+        ) {
+            FrameTimingQuery.getFrameData(
+                session = this,
+                captureApiLevel = 33,
+                packageName = "androidx.constraintlayout.compose.integration.macrobenchmark.target"
+            )
+        }
+
+        // although there are 58 frames in the trace, the last 4
+        // don't have associated complete expected/actual events
+        assertEquals(54, frameData.size)
+
+        // first frame, with matching IDs
+        frameData.single {
+            it.rtSlice.frameId == 1370854
+        }.run {
+            assertEquals(1370854, this.uiSlice.frameId)
+            assertEquals(1370854, this.expectedSlice!!.frameId)
+            assertEquals(1370854, this.actualSlice!!.frameId)
+        }
+
+        // second frame, where IDs don't match
+        frameData.single {
+            it.rtSlice.frameId == 1370869
+        }.run {
+            assertEquals(1370869, this.uiSlice.frameId) // matches
+            assertEquals(1370876, this.expectedSlice!!.frameId) // doesn't match!
+            assertEquals(1370876, this.actualSlice!!.frameId) // doesn't match!
+        }
+
+        assertEquals(
+            // Note: it's correct for UI to be > CPU in cases below,
+            // since UI is be sleeping after RT is done
+            expected = mapOf(
+                FrameDurationCpuNs to listOf(7304479L, 7567188L, 8064897L, 8434115L),
+                FrameDurationUiNs to listOf(4253646L, 7592761L, 8088855L, 8461876L),
+                FrameOverrunNs to listOf(-9009770L, -12199949L, -11299378L, -11708522L)
+            ),
+            actual = frameData.getFrameSubMetrics(captureApiLevel = 33).mapValues {
+                it.value.subList(0, 4)
+            }
+        )
+    }
 }
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
index ff8666e..385b460 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
@@ -16,12 +16,14 @@
 
 package androidx.benchmark.perfetto
 
+import androidx.benchmark.Outputs
 import androidx.benchmark.Shell
 import androidx.benchmark.macro.createTempFileFromAsset
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
+import java.io.File
 import java.net.ConnectException
 import java.net.HttpURLConnection
 import java.net.URL
@@ -250,4 +252,26 @@
         // Check server is not running
         assertTrue(!isRunning())
     }
+
+    @Test
+    fun parseLongTrace() {
+        val traceFile = File
+            .createTempFile("long_trace", ".trace", Outputs.dirUsableByAppAndShell)
+            .apply {
+                var length = 0L
+                val out = outputStream()
+                while (length < 70 * 1024 * 1024) {
+                    length += InstrumentationRegistry
+                        .getInstrumentation()
+                        .context
+                        .assets
+                        .open("api31_startup_cold.perfetto-trace")
+                        .copyTo(out)
+                }
+            }
+        PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
+            // This would throw an exception if there is an error in the parsing.
+            getTraceMetrics("android_startup")
+        }
+    }
 }
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index d839e8f..d19339e 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -302,18 +302,14 @@
 private fun extractProfile(packageName: String): String {
 
     val dumpCommand = "pm dump-profiles --dump-classes-and-methods $packageName"
-    if (BuildCompat.isAtLeastU()) {
-        // On api 34 this will produce an output like:
-        // Profile saved to '/data/misc/profman/<PACKAGE_NAME>-primary.prof.txt'
-        val stdout = Shell.executeScriptCaptureStdout(dumpCommand).trim()
-        val expected = "Profile saved to '/data/misc/profman/$packageName-primary.prof.txt'"
-        check(stdout == expected) {
-            "Expected `pm dump-profiles` stdout to be $expected but was $stdout"
-        }
-    } else {
-        // On api 33 and below this command does not produce any output
-        Shell.executeScriptSilent(dumpCommand)
+    val stdout = Shell.executeScriptCaptureStdout(dumpCommand).trim()
+    val expected = "Profile saved to '/data/misc/profman/$packageName-primary.prof.txt'"
+
+    // Output of profman was empty in previous version and can be `expected` on newer versions.
+    check(stdout.isBlank() || stdout == expected) {
+        "Expected `pm dump-profiles` stdout to be either black or `$expected` but was $stdout"
     }
+
     val fileName = "$packageName-primary.prof.txt"
     Shell.executeScriptSilent(
         "mv /data/misc/profman/$fileName ${Outputs.dirUsableByAppAndShell}/"
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index f60a946..8a73eea 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -101,8 +101,8 @@
                         val output = Shell.executeScriptCaptureStdout(
                             "cmd package compile --reset $packageName"
                         )
-                        check(output.trim() == "Success") {
-                            "Unable to recompile $packageName ($output)"
+                        check(output.trim() == "Success" || output.contains("PERFORMED")) {
+                            "Unable to recompile $packageName (out=$output)"
                         }
                     } else {
                         // User builds pre-U. Kick off a full uninstall-reinstall
@@ -154,8 +154,8 @@
                 // correctly installed. (b/231294733)
                 output = Shell.executeScriptCaptureStdout("pm install -t $tempApkPathsString")
 
-                check(output.trim() == "Success") {
-                    "Unable to install $packageName ($output)"
+                check(output.trim() == "Success" || output.contains("PERFORMED")) {
+                    "Unable to install $packageName (out=$output)"
                 }
             } finally {
                 // Cleanup the temporary APK
@@ -433,7 +433,9 @@
             val stdout = Shell.executeScriptCaptureStdout(
                 "cmd package compile -f -m $compileArgument $packageName"
             )
-            check(stdout.trim() == "Success")
+            check(stdout.trim() == "Success" || stdout.contains("PERFORMED")) {
+                "Failed to compile (out=$stdout)"
+            }
         }
     }
 }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index 6c6c458..36241de 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -28,6 +28,7 @@
 import androidx.benchmark.macro.perfetto.BatteryDischargeQuery
 import androidx.benchmark.macro.perfetto.FrameTimingQuery
 import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric
+import androidx.benchmark.macro.perfetto.FrameTimingQuery.getFrameSubMetrics
 import androidx.benchmark.macro.perfetto.MemoryCountersQuery
 import androidx.benchmark.macro.perfetto.PowerQuery
 import androidx.benchmark.macro.perfetto.StartupTimingQuery
@@ -193,11 +194,12 @@
         captureInfo: CaptureInfo,
         traceSession: PerfettoTraceProcessor.Session
     ): List<Measurement> {
-        return FrameTimingQuery.getFrameSubMetrics(
+        return FrameTimingQuery.getFrameData(
             session = traceSession,
             captureApiLevel = Build.VERSION.SDK_INT,
             packageName = captureInfo.targetPackageName
         )
+            .getFrameSubMetrics(Build.VERSION.SDK_INT)
             .filterKeys { it == SubMetric.FrameDurationCpuNs || it == SubMetric.FrameOverrunNs }
             .map {
                 Measurement(
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
index ac49d29..d0446bc 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
@@ -83,7 +83,7 @@
      *
      * Nullable slices are always present on API 31+
      */
-    private class FrameData(
+    internal class FrameData(
         val uiSlice: Slice,
         val rtSlice: Slice,
         val expectedSlice: Slice?,
@@ -93,7 +93,10 @@
             return when (subMetric) {
                 SubMetric.FrameDurationCpuNs -> rtSlice.endTs - uiSlice.ts
                 SubMetric.FrameDurationUiNs -> uiSlice.dur
-                SubMetric.FrameOverrunNs -> actualSlice!!.endTs - expectedSlice!!.endTs
+                SubMetric.FrameOverrunNs -> {
+                    // workaround b/279088460, where actual slice ends too early
+                    maxOf(actualSlice!!.endTs, rtSlice.endTs) - expectedSlice!!.endTs
+                }
             }
         }
         companion object {
@@ -141,11 +144,11 @@
         }
     }
 
-    fun getFrameSubMetrics(
+    internal fun getFrameData(
         session: PerfettoTraceProcessor.Session,
         captureApiLevel: Int,
         packageName: String,
-    ): Map<SubMetric, List<Long>> {
+    ): List<FrameData> {
         val queryResultIterator = session.query(
             query = getFullQuery(packageName)
         )
@@ -173,7 +176,7 @@
         val expectedSlices = groupedData.getOrElse(FrameSliceType.Expected) { listOf() }
 
         if (uiSlices.isEmpty()) {
-            return emptyMap()
+            return emptyList()
         }
 
         // check data looks reasonable
@@ -181,17 +184,40 @@
         require(actualSlices.isEmpty() == newSlicesShouldBeEmpty)
         require(expectedSlices.isEmpty() == newSlicesShouldBeEmpty)
 
-        val frameData = if (captureApiLevel >= 31) {
+        return if (captureApiLevel >= 31) {
             // No slice should be missing a frameId
             require(slices.none { it.frameId == null })
+
+            val actualSlicesPool = actualSlices.toMutableList()
             rtSlices.mapNotNull { rtSlice ->
                 val frameId = rtSlice.frameId!!
-                FrameData.tryCreate31(
-                    uiSlice = uiSlices.binarySearchFrameId(frameId),
-                    rtSlice = rtSlice,
-                    expectedSlice = expectedSlices.binarySearchFrameId(frameId),
-                    actualSlice = actualSlices.binarySearchFrameId(frameId)
-                )
+
+                val uiSlice = uiSlices.binarySearchFrameId(frameId)
+
+                // Ideally, we'd rely on frameIds, but these can fall out of sync due to b/279088460
+                //     expectedSlice = expectedSlices.binarySearchFrameId(frameId),
+                //     actualSlice = actualSlices.binarySearchFrameId(frameId)
+                // A pool of actual slices is used to prevent incorrect duplicate mapping. At the
+                //     end of the trace, the synthetic expect/actual slices may be missing even if
+                //     the complete end of frame is present, and we want to discard those. This
+                //     doesn't happen at front of trace, since we find actuals from the end.
+                if (uiSlice != null) {
+                    // Use fixed offset since synthetic tracepoint for actual may start after the
+                    // actual UI slice (have observed 2us in practice)
+                    val actualSlice = actualSlicesPool.lastOrNull { it.ts < uiSlice.ts + 50_000 }
+                    actualSlicesPool.remove(actualSlice)
+                    val expectedSlice = actualSlice?.frameId?.run {
+                        expectedSlices.binarySearchFrameId(this)
+                    }
+                    FrameData.tryCreate31(
+                        uiSlice = uiSlice,
+                        rtSlice = rtSlice,
+                        expectedSlice = expectedSlice,
+                        actualSlice = actualSlice
+                    )
+                } else {
+                    null
+                }
             }
         } else {
             require(slices.none { it.frameId != null })
@@ -202,11 +228,13 @@
                 )
             }
         }
+    }
 
+    fun List<FrameData>.getFrameSubMetrics(captureApiLevel: Int): Map<SubMetric, List<Long>> {
         return SubMetric.values()
             .filter { it.supportedOnApiLevel(captureApiLevel) }
             .associateWith { subMetric ->
-                frameData.map { frame -> frame.get(subMetric) }
+                map { frame -> frame.get(subMetric) }
             }
     }
 }
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
index de174db..e62dbec 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
@@ -16,6 +16,7 @@
 
 package androidx.benchmark.macro.perfetto.server
 
+import android.annotation.SuppressLint
 import android.os.Build
 import android.security.NetworkSecurityPolicy
 import android.util.Log
@@ -60,6 +61,10 @@
         private const val READ_TIMEOUT_SECONDS = 300000
         private const val SERVER_PROCESS_NAME = "trace_processor_shell"
 
+        // Note that trace processor http server has a hard limit of 64Mb for payload size.
+        // https://cs.android.com/android/platform/superproject/+/master:external/perfetto/src/base/http/http_server.cc;l=33
+        private const val PARSE_PAYLOAD_SIZE = 16 * 1024 * 1024 // 16Mb
+
         private var shellScript: ShellScript? = null
 
         /**
@@ -95,6 +100,7 @@
      *
      * @throws IllegalStateException if the server is not running by the end of the timeout.
      */
+    @SuppressLint("BanThreadSleep")
     fun startServer() = userspaceTrace("PerfettoHttpServer#startServer") {
         if (processId != null) {
             Log.w(TAG, "Tried to start a trace shell processor that is already running.")
@@ -212,13 +218,21 @@
      * Parses the trace file in chunks. Note that [notifyEof] should be called at the end to let
      * the processor know that no more chunks will be sent.
      */
-    fun parse(bytes: ByteArray): AppendTraceDataResult =
-        httpRequest(
-            method = METHOD_POST,
-            url = PATH_PARSE,
-            encodeBlock = { it.write(bytes) },
-            decodeBlock = { AppendTraceDataResult.ADAPTER.decode(it) }
-        )
+    fun parse(inputStream: InputStream): List<AppendTraceDataResult> {
+        val responses = mutableListOf<AppendTraceDataResult>()
+        while (true) {
+            val buffer = ByteArray(PARSE_PAYLOAD_SIZE)
+            val read = inputStream.read(buffer)
+            if (read <= 0) break
+            responses.add(httpRequest(
+                method = METHOD_POST,
+                url = PATH_PARSE,
+                encodeBlock = { it.write(buffer, 0, read) },
+                decodeBlock = { AppendTraceDataResult.ADAPTER.decode(it) }
+            ))
+        }
+        return responses
+    }
 
     /**
      * Notifies that the entire trace has been uploaded and no more chunks will be sent.
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
index 64d2eef..9314bde 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
@@ -21,6 +21,7 @@
 import androidx.benchmark.macro.perfetto.server.PerfettoHttpServer
 import androidx.benchmark.userspaceTrace
 import java.io.File
+import java.io.FileInputStream
 import java.io.InputStream
 import org.intellij.lang.annotations.Language
 import perfetto.protos.QueryResult
@@ -292,10 +293,8 @@
                 clearTrace()
             }
 
-            val parseResult = perfettoHttpServer.parse(traceFile.readBytes())
-            if (parseResult.error != null) {
-                throw IllegalStateException(parseResult.error)
-            }
+            val parseResults = perfettoHttpServer.parse(FileInputStream(traceFile))
+            parseResults.forEach { if (it.error != null) throw IllegalStateException(it.error) }
 
             // Notifies the server that it won't receive any more trace parts
             perfettoHttpServer.notifyEof()
diff --git a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
index b33734b..cc07a21 100755
--- a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
+++ b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
@@ -381,7 +381,7 @@
 
 function_lock_cpu
 
-if [ "$DEVICE" -ne "wembley" ]; then
+if [ ${DEVICE} != "wembley" ]; then
     function_lock_gpu_kgsl
 else
     echo "Unable to lock gpu clocks of $MODEL ($DEVICE)."
diff --git a/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-baseline-prof.txt b/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-baseline-prof.txt
index 59b9c7d..bac885e 100644
--- a/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-baseline-prof.txt
+++ b/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-baseline-prof.txt
@@ -1,127 +1,196 @@
-Landroidx/appcompat/view/menu/MenuPresenter$Callback;
-Landroidx/appcompat/widget/TintTypedArray;
-HSPLandroidx/appcompat/widget/TintTypedArray;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
-HSPLandroidx/appcompat/widget/TintTypedArray;->measure(Landroidx/constraintlayout/widget/ConstraintLayout$Measurer;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Z)Z
-HSPLandroidx/appcompat/widget/TintTypedArray;->solveLinearSystem(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;II)V
 Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->$r8$lambda$CNqLK7smWTFjXaIfqGSDUWf8U50(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->$r8$lambda$G2Q0ZfVkJNYXyLJAm2IZ1xq3Lto(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
 HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;-><init>()V
 HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onCreate(Landroid/os/Bundle;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume$lambda$1$lambda$0(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume$lambda$1(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
 HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume()V
-Landroidx/collection/ArrayMap$1;
-HSPLandroidx/collection/ArrayMap$1;-><init>()V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda0;-><init>(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda0;->run()V
+Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;-><init>(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;->run()V
 Landroidx/concurrent/futures/AbstractResolvableFuture;
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture;-><clinit>()V
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture;-><init>()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->afterDone()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->clearListeners(Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;
 PLandroidx/concurrent/futures/AbstractResolvableFuture;->complete(Landroidx/concurrent/futures/AbstractResolvableFuture;)V
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture;->get()Ljava/lang/Object;
 PLandroidx/concurrent/futures/AbstractResolvableFuture;->getDoneValue(Ljava/lang/Object;)Ljava/lang/Object;
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->releaseWaiters()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->set(Ljava/lang/Object;)Z
+Landroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;-><init>()V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;-><init>(Landroidx/concurrent/futures/AbstractResolvableFuture$1;)V
 Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;
 PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><clinit>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><init>()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><init>(Ljava/lang/Runnable;Ljava/util/concurrent/Executor;)V
 Landroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;-><init>(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;)V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casListeners(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Z
+PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casListeners(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Z
 PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casValue(Landroidx/concurrent/futures/AbstractResolvableFuture;Ljava/lang/Object;Ljava/lang/Object;)Z
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casWaiters(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)Z
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->putNext(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)V
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->putThread(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Ljava/lang/Thread;)V
+Landroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper$$ExternalSyntheticBackportWithForwarding0;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper$$ExternalSyntheticBackportWithForwarding0;->m(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
+Landroidx/concurrent/futures/AbstractResolvableFuture$SetFuture;
 Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><clinit>()V
 HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>()V
-HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>(I)V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>(Z)V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;->setNext(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)V
+PLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;->unpark()V
 Landroidx/concurrent/futures/ResolvableFuture;
 HSPLandroidx/concurrent/futures/ResolvableFuture;-><init>()V
+HSPLandroidx/concurrent/futures/ResolvableFuture;->create()Landroidx/concurrent/futures/ResolvableFuture;
+PLandroidx/concurrent/futures/ResolvableFuture;->set(Ljava/lang/Object;)Z
 Landroidx/constraintlayout/solver/ArrayLinkedVariables;
-HSPLandroidx/constraintlayout/solver/ArrayLinkedVariables;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/ArrayLinkedVariables;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/ArrayLinkedVariables;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/constraintlayout/solver/Cache;)V
 Landroidx/constraintlayout/solver/ArrayRow;
 HSPLandroidx/constraintlayout/solver/ArrayRow;-><init>()V
-HSPLandroidx/constraintlayout/solver/ArrayRow;-><init>(Landroidx/collection/ArrayMap$1;)V
-HSPLandroidx/constraintlayout/solver/ArrayRow;->addError(Landroidx/constraintlayout/solver/LinearSystem;I)V
-HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowGreaterThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)V
-HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowLowerThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;-><init>(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->addError(Landroidx/constraintlayout/solver/LinearSystem;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->addSingleError(Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->chooseSubject(Landroidx/constraintlayout/solver/LinearSystem;)Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->chooseSubjectInVariables(Landroidx/constraintlayout/solver/LinearSystem;)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowCentering(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;IFLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowEquals(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowGreaterThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowLowerThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->ensurePositiveConstant()V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->getKey()Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->hasKeyVariable()Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->isEmpty()Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->isNew(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/LinearSystem;)Z
 HSPLandroidx/constraintlayout/solver/ArrayRow;->pivot(Landroidx/constraintlayout/solver/SolverVariable;)V
 HSPLandroidx/constraintlayout/solver/ArrayRow;->reset()V
-HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/solver/SolverVariable;Z)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/SolverVariable;Z)V
 HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromRow(Landroidx/constraintlayout/solver/ArrayRow;Z)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromSystem(Landroidx/constraintlayout/solver/LinearSystem;)V
 Landroidx/constraintlayout/solver/ArrayRow$ArrayRowVariables;
+Landroidx/constraintlayout/solver/Cache;
+HSPLandroidx/constraintlayout/solver/Cache;-><init>()V
 Landroidx/constraintlayout/solver/LinearSystem;
+HSPLandroidx/constraintlayout/solver/LinearSystem;-><clinit>()V
 HSPLandroidx/constraintlayout/solver/LinearSystem;-><init>()V
-HSPLandroidx/constraintlayout/solver/LinearSystem;->acquireSolverVariable$enumunboxing$(I)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->acquireSolverVariable(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)Landroidx/constraintlayout/solver/SolverVariable;
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addCentering(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;IFLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addConstraint(Landroidx/constraintlayout/solver/ArrayRow;)V
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addEquality(Landroidx/constraintlayout/solver/SolverVariable;I)V
-HSPLandroidx/constraintlayout/solver/LinearSystem;->addEquality(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addEquality(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)Landroidx/constraintlayout/solver/ArrayRow;
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addGreaterThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addLowerThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
 HSPLandroidx/constraintlayout/solver/LinearSystem;->addRow(Landroidx/constraintlayout/solver/ArrayRow;)V
-HSPLandroidx/constraintlayout/solver/LinearSystem;->createErrorVariable(I)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addSingleError(Landroidx/constraintlayout/solver/ArrayRow;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->computeValues()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->createErrorVariable(ILjava/lang/String;)Landroidx/constraintlayout/solver/SolverVariable;
 HSPLandroidx/constraintlayout/solver/LinearSystem;->createObjectVariable(Ljava/lang/Object;)Landroidx/constraintlayout/solver/SolverVariable;
 HSPLandroidx/constraintlayout/solver/LinearSystem;->createRow()Landroidx/constraintlayout/solver/ArrayRow;
 HSPLandroidx/constraintlayout/solver/LinearSystem;->createSlackVariable()Landroidx/constraintlayout/solver/SolverVariable;
-HSPLandroidx/constraintlayout/solver/LinearSystem;->getObjectVariableValue(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;)I
+HSPLandroidx/constraintlayout/solver/LinearSystem;->enforceBFS(Landroidx/constraintlayout/solver/LinearSystem$Row;)I
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getCache()Landroidx/constraintlayout/solver/Cache;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getMetrics()Landroidx/constraintlayout/solver/Metrics;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getObjectVariableValue(Ljava/lang/Object;)I
 HSPLandroidx/constraintlayout/solver/LinearSystem;->increaseTableSize()V
-HSPLandroidx/constraintlayout/solver/LinearSystem;->optimize(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->minimize()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->minimizeGoal(Landroidx/constraintlayout/solver/LinearSystem$Row;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->optimize(Landroidx/constraintlayout/solver/LinearSystem$Row;Z)I
 HSPLandroidx/constraintlayout/solver/LinearSystem;->releaseRows()V
 HSPLandroidx/constraintlayout/solver/LinearSystem;->reset()V
+Landroidx/constraintlayout/solver/LinearSystem$Row;
 Landroidx/constraintlayout/solver/LinearSystem$ValuesRow;
-HSPLandroidx/constraintlayout/solver/LinearSystem$ValuesRow;-><init>(Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem$ValuesRow;-><init>(Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/Pools$Pool;
 Landroidx/constraintlayout/solver/Pools$SimplePool;
-HSPLandroidx/constraintlayout/solver/Pools$SimplePool;-><init>()V
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;-><init>(I)V
 HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->acquire()Ljava/lang/Object;
-HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->release(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->release(Ljava/lang/Object;)Z
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->releaseAll([Ljava/lang/Object;I)V
 Landroidx/constraintlayout/solver/PriorityGoalRow;
-HSPLandroidx/constraintlayout/solver/PriorityGoalRow;-><init>(Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;-><init>(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->addError(Landroidx/constraintlayout/solver/SolverVariable;)V
 HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->addToGoal(Landroidx/constraintlayout/solver/SolverVariable;)V
-HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->getPivotCandidate([Z)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->clear()V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->getPivotCandidate(Landroidx/constraintlayout/solver/LinearSystem;[Z)Landroidx/constraintlayout/solver/SolverVariable;
 HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->removeGoal(Landroidx/constraintlayout/solver/SolverVariable;)V
 HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->updateFromRow(Landroidx/constraintlayout/solver/ArrayRow;Z)V
 Landroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;
-HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;-><init>(Landroidx/constraintlayout/solver/PriorityGoalRow;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;-><init>(Landroidx/constraintlayout/solver/PriorityGoalRow;Landroidx/constraintlayout/solver/PriorityGoalRow;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->addToGoal(Landroidx/constraintlayout/solver/SolverVariable;F)Z
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->init(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->isNegative()Z
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->reset()V
 Landroidx/constraintlayout/solver/SolverVariable;
-HSPLandroidx/constraintlayout/solver/SolverVariable;-><init>(I)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariable;-><init>(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)V
 HSPLandroidx/constraintlayout/solver/SolverVariable;->addToRow(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->increaseErrorId()V
 HSPLandroidx/constraintlayout/solver/SolverVariable;->removeFromRow(Landroidx/constraintlayout/solver/ArrayRow;)V
 HSPLandroidx/constraintlayout/solver/SolverVariable;->reset()V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->setFinalValue(Landroidx/constraintlayout/solver/LinearSystem;F)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->setType(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)V
 HSPLandroidx/constraintlayout/solver/SolverVariable;->updateReferencesWithNewDefinition(Landroidx/constraintlayout/solver/ArrayRow;)V
-Landroidx/constraintlayout/solver/SolverVariable$Type$EnumUnboxingSharedUtility;
-HSPLandroidx/constraintlayout/solver/SolverVariable$Type$EnumUnboxingSharedUtility;-><clinit>()V
-HSPLandroidx/constraintlayout/solver/SolverVariable$Type$EnumUnboxingSharedUtility;->ordinal(I)I
+Landroidx/constraintlayout/solver/SolverVariable$Type;
+HSPLandroidx/constraintlayout/solver/SolverVariable$Type;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariable$Type;-><init>(Ljava/lang/String;I)V
 Landroidx/constraintlayout/solver/SolverVariableValues;
-HSPLandroidx/constraintlayout/solver/SolverVariableValues;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/constraintlayout/solver/Cache;)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->add(Landroidx/constraintlayout/solver/SolverVariable;FZ)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->addToHashMap(Landroidx/constraintlayout/solver/SolverVariable;I)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->addVariable(ILandroidx/constraintlayout/solver/SolverVariable;F)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->clear()V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->divideByAmount(F)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->findEmptySlot()I
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->get(Landroidx/constraintlayout/solver/SolverVariable;)F
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getCurrentSize()I
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getVariable(I)Landroidx/constraintlayout/solver/SolverVariable;
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getVariableValue(I)F
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->indexOf(Landroidx/constraintlayout/solver/SolverVariable;)I
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->insertVariable(ILandroidx/constraintlayout/solver/SolverVariable;F)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->invert()V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->put(Landroidx/constraintlayout/solver/SolverVariable;F)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->remove(Landroidx/constraintlayout/solver/SolverVariable;Z)F
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->removeFromHashMap(Landroidx/constraintlayout/solver/SolverVariable;)V
 HSPLandroidx/constraintlayout/solver/SolverVariableValues;->use(Landroidx/constraintlayout/solver/ArrayRow;Z)F
 Landroidx/constraintlayout/solver/widgets/Barrier;
 Landroidx/constraintlayout/solver/widgets/ChainHead;
 Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;)V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->connect(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;II)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->connect(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;IIZ)Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getMargin()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getSolverVariable()Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getTarget()Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->isConnected()Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->reset()V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->resetSolverVariable()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->resetSolverVariable(Landroidx/constraintlayout/solver/Cache;)V
 Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;-><clinit>()V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;-><init>(ILjava/lang/String;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;-><init>(Ljava/lang/String;I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;->values()[Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;
 Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;-><clinit>()V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;-><init>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addAnchors()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addFirst()Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addToSolver(Landroidx/constraintlayout/solver/LinearSystem;)V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->applyConstraints$enumunboxing$(Landroidx/constraintlayout/solver/LinearSystem;ZZZZLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;IZLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;IIIIFZZZZIIIIFZ)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->applyConstraints(Landroidx/constraintlayout/solver/LinearSystem;ZZZZLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;ZLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;IIIIFZZZZIIIIFZ)V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->createObjectVariables(Landroidx/constraintlayout/solver/LinearSystem;)V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getAnchor(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;)Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getDimensionBehaviour$enumunboxing$(I)I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getBaselineDistance()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getCompanionWidget()Ljava/lang/Object;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getDimensionBehaviour(I)Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getHeight()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getHorizontalDimensionBehaviour()Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getMinHeight()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getMinWidth()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getParent()Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getVerticalDimensionBehaviour()Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getVisibility()I
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getWidth()I
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getX()I
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getY()I
@@ -130,27 +199,92 @@
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->isInHorizontalChain()Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->isInVerticalChain()Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->reset()V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->resetSolverVariables(Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->resetSolverVariables(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setBaselineDistance(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setCompanionWidget(Ljava/lang/Object;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setDimensionRatio(Ljava/lang/String;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setFrame(IIII)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHasBaseline(Z)V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHeight(I)V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalDimensionBehaviour$enumunboxing$(I)V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalDimensionBehaviour$enumunboxing$(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalBiasPercent(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalChainStyle(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalMatchStyle(IIIF)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalWeight(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setInBarrier(IZ)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMaxHeight(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMaxWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMinHeight(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMinWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setParent(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalBiasPercent(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalChainStyle(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalMatchStyle(IIIF)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalWeight(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVisibility(I)V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setX(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setY(I)V
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->updateFromSolver(Landroidx/constraintlayout/solver/LinearSystem;)V
+Landroidx/constraintlayout/solver/widgets/ConstraintWidget$1;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$1;-><clinit>()V
+Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;-><init>(Ljava/lang/String;I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;->values()[Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
 Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;-><init>()V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->addChildrenToSolver(Landroidx/constraintlayout/solver/LinearSystem;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->addChildrenToSolver(Landroidx/constraintlayout/solver/LinearSystem;)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->getMeasurer()Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->getOptimizationLevel()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->invalidateGraph()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->invalidateMeasures()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->isHeightMeasuredTooSmall()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->isWidthMeasuredTooSmall()Z
 HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->layout()V
-HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->resetSolverVariables(Landroidx/collection/ArrayMap$1;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->measure(IIIIIIIII)J
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->optimizeFor(I)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->resetChains()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setMeasurer(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setOptimizationLevel(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setRtl(Z)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->updateChildrenFromSolver(Landroidx/constraintlayout/solver/LinearSystem;[Z)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->updateHierarchy()V
 Landroidx/constraintlayout/solver/widgets/Guideline;
 Landroidx/constraintlayout/solver/widgets/Helper;
 Landroidx/constraintlayout/solver/widgets/HelperWidget;
+Landroidx/constraintlayout/solver/widgets/Optimizer;
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;->checkMatchParent(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;->enabled(II)Z
+Landroidx/constraintlayout/solver/widgets/VirtualLayout;
+Landroidx/constraintlayout/solver/widgets/WidgetContainer;
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;-><init>()V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->add(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->removeAllChildren()V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->resetSolverVariables(Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->measure(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Z)Z
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->measureChildren(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->solveLinearSystem(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;Ljava/lang/String;II)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->solverMeasure(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;IIIIIIIII)J
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->updateHierarchy(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
 Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;-><init>()V
+Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;
 Landroidx/constraintlayout/solver/widgets/analyzer/Dependency;
 Landroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->invalidateGraph()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->invalidateMeasures()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->setMeasurer(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;)V
 Landroidx/constraintlayout/solver/widgets/analyzer/DependencyNode;
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode;-><init>(Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;-><init>(Ljava/lang/String;I)V
 Landroidx/constraintlayout/solver/widgets/analyzer/DimensionDependency;
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/DimensionDependency;-><init>(Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;)V
 Landroidx/constraintlayout/solver/widgets/analyzer/HorizontalWidgetRun;
@@ -160,22 +294,35 @@
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/VerticalWidgetRun;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
 Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;
 HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;-><init>(Ljava/lang/String;I)V
 Landroidx/constraintlayout/widget/ConstraintHelper;
 Landroidx/constraintlayout/widget/ConstraintLayout;
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->access$000(Landroidx/constraintlayout/widget/ConstraintLayout;)Ljava/util/ArrayList;
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->addView(Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->applyConstraintsFromLayoutParams(ZLandroid/view/View;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;Landroid/util/SparseArray;)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->checkLayoutParams(Landroid/view/ViewGroup$LayoutParams;)Z
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->dispatchDraw(Landroid/graphics/Canvas;)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->generateLayoutParams(Landroid/util/AttributeSet;)Landroid/view/ViewGroup$LayoutParams;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->generateLayoutParams(Landroid/util/AttributeSet;)Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->getPaddingWidth()I
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->getViewWidget(Landroid/view/View;)Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->init(Landroid/util/AttributeSet;II)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->isRtl()Z
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->markHierarchyDirty()V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onLayout(ZIIII)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onMeasure(II)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onViewAdded(Landroid/view/View;)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->requestLayout()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveMeasuredDimension(IIIIZZ)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveSystem(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;III)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setChildrenConstraints()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setSelfDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;IIII)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout;->updateHierarchy()Z
+Landroidx/constraintlayout/widget/ConstraintLayout$1;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$1;-><clinit>()V
 Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;
 HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->resolveLayoutDirection(I)V
@@ -183,90 +330,210 @@
 Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;
 HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;-><clinit>()V
 Landroidx/constraintlayout/widget/ConstraintLayout$Measurer;
-HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;-><init>(Landroidx/constraintlayout/widget/ConstraintLayout;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;-><init>(Landroidx/constraintlayout/widget/ConstraintLayout;Landroidx/constraintlayout/widget/ConstraintLayout;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->captureLayoutInfos(IIIIII)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->didMeasures()V
 HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->measure(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;)V
+Landroidx/constraintlayout/widget/ConstraintLayoutStates;
+Landroidx/constraintlayout/widget/ConstraintSet;
 Landroidx/constraintlayout/widget/Guideline;
+Landroidx/constraintlayout/widget/Placeholder;
 Landroidx/constraintlayout/widget/R$styleable;
 HSPLandroidx/constraintlayout/widget/R$styleable;-><clinit>()V
+Landroidx/constraintlayout/widget/VirtualLayout;
 Landroidx/core/app/CoreComponentFactory;
 HSPLandroidx/core/app/CoreComponentFactory;-><init>()V
+HSPLandroidx/core/app/CoreComponentFactory;->checkCompatWrapper(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLandroidx/core/app/CoreComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;
 HSPLandroidx/core/app/CoreComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;
 HSPLandroidx/core/app/CoreComponentFactory;->instantiateProvider(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/content/ContentProvider;
 PLandroidx/core/app/CoreComponentFactory;->instantiateReceiver(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/content/BroadcastReceiver;
-Landroidx/core/view/ViewCompat$$ExternalSyntheticApiModelOutline2;
-HSPLandroidx/core/view/ViewCompat$$ExternalSyntheticApiModelOutline2;->m()Landroid/view/Choreographer;
-HSPLandroidx/core/view/ViewCompat$$ExternalSyntheticApiModelOutline2;->m(Landroid/view/Choreographer;Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;)V
-Landroidx/core/view/ViewCompat$3$$ExternalSyntheticApiModelOutline0;
-HSPLandroidx/core/view/ViewCompat$3$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Looper;)Landroid/os/Handler;
+Landroidx/core/app/CoreComponentFactory$CompatWrapped;
+Landroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m$1(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m$2(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m(Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+Landroidx/core/os/TraceCompat$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/core/os/TraceCompat$$ExternalSyntheticApiModelOutline0;->m()Z
+Landroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m()Landroid/view/Choreographer;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Looper;)Landroid/os/Handler;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/Choreographer;Landroid/view/Choreographer$FrameCallback;)V
 PLandroidx/profileinstaller/ProfileInstallReceiver;-><init>()V
 PLandroidx/profileinstaller/ProfileInstallReceiver;->onReceive(Landroid/content/Context;Landroid/content/Intent;)V
-PLandroidx/profileinstaller/ProfileInstaller$$ExternalSyntheticLambda1;-><init>(I)V
-Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;
+PLandroidx/profileinstaller/ProfileInstallReceiver;->saveProfile(Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;-><init>()V
+PLandroidx/profileinstaller/ProfileInstallReceiver$ResultDiagnostics;-><init>(Landroidx/profileinstaller/ProfileInstallReceiver;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver$ResultDiagnostics;->onResultReceived(ILjava/lang/Object;)V
+PLandroidx/profileinstaller/ProfileInstaller;-><clinit>()V
+PLandroidx/profileinstaller/ProfileInstaller;->hasAlreadyWrittenProfileForThisInstall(Landroid/content/pm/PackageInfo;Ljava/io/File;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)Z
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)V
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Z)V
+PLandroidx/profileinstaller/ProfileInstaller$1;-><init>()V
+PLandroidx/profileinstaller/ProfileInstaller$1;->onResultReceived(ILjava/lang/Object;)V
+PLandroidx/profileinstaller/ProfileInstaller$2;-><init>()V
+PLandroidx/profileinstaller/ProfileInstaller$2;->onResultReceived(ILjava/lang/Object;)V
 Landroidx/profileinstaller/ProfileInstallerInitializer;
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer;-><init>()V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Landroidx/profileinstaller/ProfileInstallerInitializer$Result;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->delayAfterFirstFrame(Landroid/content/Context;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->dependencies()Ljava/util/List;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->installAfterDelay(Landroid/content/Context;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$delayAfterFirstFrame$0$androidx-profileinstaller-ProfileInstallerInitializer(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$installAfterDelay$1(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$writeInBackground$2(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->writeInBackground(Landroid/content/Context;)V
 Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;-><init>(ILjava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;->run()V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;-><init>(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;->run()V
 Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;-><init>(Landroid/content/Context;I)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;-><init>(Landroidx/profileinstaller/ProfileInstallerInitializer;Landroid/content/Context;)V
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;->run()V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;-><init>(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;->run()V
 Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->lambda$postFrameCallback$0(Ljava/lang/Runnable;J)V
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->postFrameCallback(Ljava/lang/Runnable;)V
 Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;-><init>(Ljava/lang/Runnable;)V
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;->doFrame(J)V
 Landroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;
 HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;->createAsync(Landroid/os/Looper;)Landroid/os/Handler;
+Landroidx/profileinstaller/ProfileInstallerInitializer$Result;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Result;-><init>()V
 Landroidx/profileinstaller/ProfileVerifier;
 HSPLandroidx/profileinstaller/ProfileVerifier;-><clinit>()V
+HSPLandroidx/profileinstaller/ProfileVerifier;->getCompilationStatusAsync()Lcom/google/common/util/concurrent/ListenableFuture;
+PLandroidx/profileinstaller/ProfileVerifier;->getPackageLastUpdateTime(Landroid/content/Context;)J
 PLandroidx/profileinstaller/ProfileVerifier;->setCompilationStatus(IZZ)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
-PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)V
+PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
 PLandroidx/profileinstaller/ProfileVerifier$Api33Impl;->getPackageInfo(Landroid/content/pm/PackageManager;Landroid/content/Context;)Landroid/content/pm/PackageInfo;
-PLandroidx/profileinstaller/ProfileVerifier$Api33Impl$$ExternalSyntheticApiModelOutline0;->m()Landroid/content/pm/PackageManager$PackageInfoFlags;
-PLandroidx/profileinstaller/ProfileVerifier$Api33Impl$$ExternalSyntheticApiModelOutline0;->m(Landroid/content/pm/PackageManager;Ljava/lang/String;Landroid/content/pm/PackageManager$PackageInfoFlags;)Landroid/content/pm/PackageInfo;
 PLandroidx/profileinstaller/ProfileVerifier$Cache;-><init>(IIJJ)V
 PLandroidx/profileinstaller/ProfileVerifier$Cache;->equals(Ljava/lang/Object;)Z
 PLandroidx/profileinstaller/ProfileVerifier$Cache;->readFromFile(Ljava/io/File;)Landroidx/profileinstaller/ProfileVerifier$Cache;
+Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
 PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;-><init>(IZZ)V
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->getProfileInstallResultCode()I
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->hasProfileEnqueuedForCompilation()Z
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->isCompiledWithProfile()Z
 Landroidx/startup/AppInitializer;
 HSPLandroidx/startup/AppInitializer;-><clinit>()V
 HSPLandroidx/startup/AppInitializer;-><init>(Landroid/content/Context;)V
+HSPLandroidx/startup/AppInitializer;->discoverAndInitialize()V
 HSPLandroidx/startup/AppInitializer;->discoverAndInitialize(Landroid/os/Bundle;)V
-HSPLandroidx/startup/AppInitializer;->doInitialize(Ljava/lang/Class;Ljava/util/HashSet;)V
+HSPLandroidx/startup/AppInitializer;->doInitialize(Ljava/lang/Class;Ljava/util/Set;)Ljava/lang/Object;
+HSPLandroidx/startup/AppInitializer;->getInstance(Landroid/content/Context;)Landroidx/startup/AppInitializer;
 Landroidx/startup/InitializationProvider;
 HSPLandroidx/startup/InitializationProvider;-><init>()V
 HSPLandroidx/startup/InitializationProvider;->onCreate()Z
+Landroidx/startup/Initializer;
+Landroidx/startup/R$string;
 Landroidx/tracing/Trace;
+HSPLandroidx/tracing/Trace;->beginSection(Ljava/lang/String;)V
+HSPLandroidx/tracing/Trace;->endSection()V
 HSPLandroidx/tracing/Trace;->isEnabled()Z
-Landroidx/tracing/Trace$$ExternalSyntheticApiModelOutline0;
-HSPLandroidx/tracing/Trace$$ExternalSyntheticApiModelOutline0;->m()Z
-Landroidx/tracing/TraceApi18Impl$$ExternalSyntheticApiModelOutline0;
-HSPLandroidx/tracing/TraceApi18Impl$$ExternalSyntheticApiModelOutline0;->m()V
-HSPLandroidx/tracing/TraceApi18Impl$$ExternalSyntheticApiModelOutline0;->m(Ljava/lang/String;)V
+Landroidx/tracing/TraceApi18Impl;
+HSPLandroidx/tracing/TraceApi18Impl;->beginSection(Ljava/lang/String;)V
+HSPLandroidx/tracing/TraceApi18Impl;->endSection()V
+Lcom/google/common/util/concurrent/ListenableFuture;
 PLkotlin/Pair;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-Lkotlin/TuplesKt;
-HSPLkotlin/TuplesKt;-><clinit>()V
-HSPLkotlin/TuplesKt;-><init>(Ljava/lang/Object;)V
-PLkotlin/TuplesKt;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V
-HSPLkotlin/TuplesKt;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V
-PLkotlin/TuplesKt;->writeProfile(Landroid/content/Context;Landroidx/profileinstaller/ProfileInstaller$$ExternalSyntheticLambda1;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Z)V
-PLkotlin/jvm/internal/Lambda;-><init>()V
+PLkotlin/Pair;->component1()Ljava/lang/Object;
+PLkotlin/Pair;->component2()Ljava/lang/Object;
+PLkotlin/Pair;->getFirst()Ljava/lang/Object;
+PLkotlin/Pair;->getSecond()Ljava/lang/Object;
+PLkotlin/TuplesKt;->to(Ljava/lang/Object;Ljava/lang/Object;)Lkotlin/Pair;
+PLkotlin/collections/ArraysKt___ArraysJvmKt;->asList([Ljava/lang/Object;)Ljava/util/List;
+PLkotlin/collections/ArraysUtilJVM;->asList([Ljava/lang/Object;)Ljava/util/List;
+PLkotlin/collections/CollectionsKt__CollectionsKt;->getLastIndex(Ljava/util/List;)I
+PLkotlin/collections/CollectionsKt__CollectionsKt;->optimizeReadOnlyList(Ljava/util/List;)Ljava/util/List;
+PLkotlin/collections/CollectionsKt__IterablesKt;->collectionSizeOrDefault(Ljava/lang/Iterable;I)I
+PLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo$default(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Appendable;
+PLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Appendable;
+PLkotlin/collections/CollectionsKt___CollectionsKt;->minOrNull(Ljava/lang/Iterable;)Ljava/lang/Comparable;
+PLkotlin/collections/IntIterator;-><init>()V
+PLkotlin/internal/ProgressionUtilKt;->differenceModulo(III)I
+PLkotlin/internal/ProgressionUtilKt;->getProgressionLastElement(III)I
+PLkotlin/internal/ProgressionUtilKt;->mod(II)I
+Lkotlin/jvm/internal/Intrinsics;
+PLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;Ljava/lang/String;)V
+PLkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V
+HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V
+PLkotlin/jvm/internal/Lambda;-><init>(I)V
+PLkotlin/ranges/IntProgression;-><clinit>()V
 PLkotlin/ranges/IntProgression;-><init>(III)V
+PLkotlin/ranges/IntProgression;->getFirst()I
+PLkotlin/ranges/IntProgression;->getLast()I
+PLkotlin/ranges/IntProgression;->getStep()I
 PLkotlin/ranges/IntProgression;->iterator()Ljava/util/Iterator;
+PLkotlin/ranges/IntProgression;->iterator()Lkotlin/collections/IntIterator;
+PLkotlin/ranges/IntProgression$Companion;-><init>()V
+PLkotlin/ranges/IntProgression$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
 PLkotlin/ranges/IntProgressionIterator;-><init>(III)V
+PLkotlin/ranges/IntProgressionIterator;->hasNext()Z
+PLkotlin/ranges/IntProgressionIterator;->nextInt()I
 PLkotlin/ranges/IntRange;-><clinit>()V
 PLkotlin/ranges/IntRange;-><init>(II)V
-Lkotlin/ranges/IntRange$Companion;
-HSPLkotlin/ranges/IntRange$Companion;-><init>(I)V
-PLkotlin/ranges/IntRange$Companion;->onResultReceived(ILjava/lang/Object;)V
-PLkotlin/text/DelimitedRangesSequence;-><init>(Ljava/lang/String;IILkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;)V
+PLkotlin/ranges/IntRange;->getEndInclusive()Ljava/lang/Integer;
+PLkotlin/ranges/IntRange;->getStart()Ljava/lang/Integer;
+PLkotlin/ranges/IntRange$Companion;-><init>()V
+PLkotlin/ranges/IntRange$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+PLkotlin/ranges/RangesKt___RangesKt;->coerceAtLeast(II)I
+PLkotlin/ranges/RangesKt___RangesKt;->coerceAtMost(II)I
+PLkotlin/ranges/RangesKt___RangesKt;->coerceIn(III)I
+PLkotlin/ranges/RangesKt___RangesKt;->until(II)Lkotlin/ranges/IntRange;
+PLkotlin/sequences/SequencesKt___SequencesKt;->map(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toCollection(Lkotlin/sequences/Sequence;Ljava/util/Collection;)Ljava/util/Collection;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toMutableList(Lkotlin/sequences/Sequence;)Ljava/util/List;
+PLkotlin/sequences/TransformingSequence;-><init>(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)V
+PLkotlin/sequences/TransformingSequence;->access$getSequence$p(Lkotlin/sequences/TransformingSequence;)Lkotlin/sequences/Sequence;
+PLkotlin/sequences/TransformingSequence;->access$getTransformer$p(Lkotlin/sequences/TransformingSequence;)Lkotlin/jvm/functions/Function1;
+PLkotlin/sequences/TransformingSequence;->iterator()Ljava/util/Iterator;
+PLkotlin/sequences/TransformingSequence$iterator$1;-><init>(Lkotlin/sequences/TransformingSequence;)V
+PLkotlin/sequences/TransformingSequence$iterator$1;->hasNext()Z
+PLkotlin/sequences/TransformingSequence$iterator$1;->next()Ljava/lang/Object;
+PLkotlin/text/CharsKt__CharJVMKt;->isWhitespace(C)Z
+PLkotlin/text/DelimitedRangesSequence;-><init>(Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function2;)V
+PLkotlin/text/DelimitedRangesSequence;->access$getGetNextMatch$p(Lkotlin/text/DelimitedRangesSequence;)Lkotlin/jvm/functions/Function2;
+PLkotlin/text/DelimitedRangesSequence;->access$getInput$p(Lkotlin/text/DelimitedRangesSequence;)Ljava/lang/CharSequence;
+PLkotlin/text/DelimitedRangesSequence;->access$getLimit$p(Lkotlin/text/DelimitedRangesSequence;)I
+PLkotlin/text/DelimitedRangesSequence;->access$getStartIndex$p(Lkotlin/text/DelimitedRangesSequence;)I
 PLkotlin/text/DelimitedRangesSequence;->iterator()Ljava/util/Iterator;
 PLkotlin/text/DelimitedRangesSequence$iterator$1;-><init>(Lkotlin/text/DelimitedRangesSequence;)V
 PLkotlin/text/DelimitedRangesSequence$iterator$1;->calcNext()V
 PLkotlin/text/DelimitedRangesSequence$iterator$1;->hasNext()Z
 PLkotlin/text/DelimitedRangesSequence$iterator$1;->next()Ljava/lang/Object;
-PLkotlin/text/StringsKt__StringsKt;->isBlank(Ljava/lang/String;)Z
+PLkotlin/text/DelimitedRangesSequence$iterator$1;->next()Lkotlin/ranges/IntRange;
+PLkotlin/text/StringsKt__AppendableKt;->appendElement(Ljava/lang/Appendable;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
+PLkotlin/text/StringsKt__IndentKt;->getIndentFunction$StringsKt__IndentKt(Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
+PLkotlin/text/StringsKt__IndentKt;->indentWidth$StringsKt__IndentKt(Ljava/lang/String;)I
+PLkotlin/text/StringsKt__IndentKt;->replaceIndent(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__IndentKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;-><clinit>()V
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;-><init>()V
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;->invoke(Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z
+PLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z
+PLkotlin/text/StringsKt__StringsKt;->access$findAnyOf(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt;->findAnyOf$StringsKt__StringsKt(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange;
+PLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I
+PLkotlin/text/StringsKt__StringsKt;->lineSequence(Ljava/lang/CharSequence;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->lines(Ljava/lang/CharSequence;)Ljava/util/List;
+PLkotlin/text/StringsKt__StringsKt;->rangesDelimitedBy$StringsKt__StringsKt$default(Ljava/lang/CharSequence;[Ljava/lang/String;IZIILjava/lang/Object;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->rangesDelimitedBy$StringsKt__StringsKt(Ljava/lang/CharSequence;[Ljava/lang/String;IZI)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->requireNonNegativeLimit(I)V
+PLkotlin/text/StringsKt__StringsKt;->splitToSequence$default(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->splitToSequence(Ljava/lang/CharSequence;[Ljava/lang/String;ZI)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->substring(Ljava/lang/CharSequence;Lkotlin/ranges/IntRange;)Ljava/lang/String;
 PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;-><init>(Ljava/util/List;Z)V
-PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;-><init>(ILjava/lang/String;)V
-PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;->invoke(Ljava/lang/Object;)Ljava/lang/String;
\ No newline at end of file
+PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;->invoke(Ljava/lang/CharSequence;I)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;-><init>(Ljava/lang/CharSequence;)V
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;->invoke(Lkotlin/ranges/IntRange;)Ljava/lang/String;
+PLkotlin/text/StringsKt___StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String;
\ No newline at end of file
diff --git a/biometric/biometric/src/main/res/values-am/strings.xml b/biometric/biometric/src/main/res/values-am/strings.xml
index a32005d..28d2673 100644
--- a/biometric/biometric/src/main/res/values-am/strings.xml
+++ b/biometric/biometric/src/main/res/values-am/strings.xml
@@ -33,15 +33,15 @@
     <string name="use_fingerprint_label" msgid="6961788485681412417">"የጣት አሻራን ተጠቀም"</string>
     <string name="use_face_label" msgid="6533512708069459542">"መልክን ተጠቀም"</string>
     <string name="use_biometric_label" msgid="6524145989441579428">"ባዮሜትሪኮችን ተጠቀም"</string>
-    <string name="use_screen_lock_label" msgid="5459869335976243512">"የማያ ገጽ መቆለፊያን ተጠቀም"</string>
-    <string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"የጣት አሻራን ወይም የማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
-    <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"የመልክ ወይም የማያ ገጽ መቆለፊያን ተጠቀም"</string>
-    <string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"ባዮሜትሪኮችን ወይም ማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
+    <string name="use_screen_lock_label" msgid="5459869335976243512">"የማያ ገፅ መቆለፊያን ተጠቀም"</string>
+    <string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"የጣት አሻራን ወይም የማያ ገፅ መቆለፊያን ይጠቀሙ"</string>
+    <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"የመልክ ወይም የማያ ገፅ መቆለፊያን ተጠቀም"</string>
+    <string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"ባዮሜትሪኮችን ወይም ማያ ገፅ መቆለፊያን ይጠቀሙ"</string>
     <string name="fingerprint_prompt_message" msgid="7449360011861769080">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string>
     <string name="face_prompt_message" msgid="2282389249605674226">"ለመቀጠል የእርስዎን መልክ ይጠቀሙ"</string>
     <string name="biometric_prompt_message" msgid="1160635338192065472">"ለመቀጠል የእርስዎን ባዮሜትሪክ ይጠቀሙ"</string>
-    <string name="screen_lock_prompt_message" msgid="5659570757430909869">"ለመቀጠል የእርስዎን የማያ ገጽ ቁልፍ ያስገቡ"</string>
-    <string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"ለመቀጠል የእርስዎን የጣት አሻራ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
-    <string name="face_or_screen_lock_prompt_message" msgid="4562557128765735254">"ለመቀጠል የእርስዎን መልክ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
-    <string name="biometric_or_screen_lock_prompt_message" msgid="2102429900219199821">"ለመቀጠል የእርስዎን የባዮሜትሪክ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
+    <string name="screen_lock_prompt_message" msgid="5659570757430909869">"ለመቀጠል የእርስዎን የማያ ገፅ ቁልፍ ያስገቡ"</string>
+    <string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"ለመቀጠል የእርስዎን የጣት አሻራ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
+    <string name="face_or_screen_lock_prompt_message" msgid="4562557128765735254">"ለመቀጠል የእርስዎን መልክ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
+    <string name="biometric_or_screen_lock_prompt_message" msgid="2102429900219199821">"ለመቀጠል የእርስዎን የባዮሜትሪክ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
 </resources>
diff --git a/biometric/biometric/src/main/res/values-ky/strings.xml b/biometric/biometric/src/main/res/values-ky/strings.xml
index 4c50879..97be746 100644
--- a/biometric/biometric/src/main/res/values-ky/strings.xml
+++ b/biometric/biometric/src/main/res/values-ky/strings.xml
@@ -32,7 +32,7 @@
     <string name="fingerprint_dialog_icon_description" msgid="5462024216548165325">"Манжа изинин сүрөтчөсү"</string>
     <string name="use_fingerprint_label" msgid="6961788485681412417">"Манжа изин колдонуу"</string>
     <string name="use_face_label" msgid="6533512708069459542">"Жүзүнөн таанып ачууну колдонуу"</string>
-    <string name="use_biometric_label" msgid="6524145989441579428">"Биометрикалык жөндөөлөрдү колдонуу"</string>
+    <string name="use_biometric_label" msgid="6524145989441579428">"Биометрикалык параметрлерди колдонуу"</string>
     <string name="use_screen_lock_label" msgid="5459869335976243512">"Экран кулпусун колдонуу"</string>
     <string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"Манжа изин же экрандын кулпусун колдонуу"</string>
     <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"Жүзүнөн таанып ачууну же экрандын кулпусун колдонуу"</string>
diff --git a/biometric/biometric/src/main/res/values-zh-rHK/strings.xml b/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
index c732b33..1be8ed3 100644
--- a/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
+++ b/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
@@ -38,7 +38,7 @@
     <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"使用面孔或螢幕鎖定"</string>
     <string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"使用生物識別或螢幕鎖定"</string>
     <string name="fingerprint_prompt_message" msgid="7449360011861769080">"請使用指紋驗證身分,才能繼續操作"</string>
-    <string name="face_prompt_message" msgid="2282389249605674226">"請使用您的面孔驗證身分,才能繼續操作"</string>
+    <string name="face_prompt_message" msgid="2282389249605674226">"請使用你的面孔驗證身分,才能繼續操作"</string>
     <string name="biometric_prompt_message" msgid="1160635338192065472">"請使用使用生物識別驗證身分,才能繼續操作"</string>
     <string name="screen_lock_prompt_message" msgid="5659570757430909869">"請輸入螢幕鎖定解鎖憑證,才能繼續操作"</string>
     <string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"請使用指紋解鎖或螢幕鎖定功能驗證身分,才能繼續操作"</string>
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattCharacteristicTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattCharacteristicTest.kt
new file mode 100644
index 0000000..fe2a87b
--- /dev/null
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattCharacteristicTest.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.bluetooth
+
+import android.bluetooth.BluetoothGattCharacteristic.PERMISSION_READ
+import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY
+import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ
+import java.util.UUID
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BluetoothGattCharacteristicTest {
+    @Test
+    fun constructorWithFwkInstance() {
+        val characteristicUuid = UUID.randomUUID()
+        val properties = PROPERTY_READ or PROPERTY_NOTIFY
+        val permissions = PERMISSION_READ
+
+        val fwkGattCharacteristic = android.bluetooth.BluetoothGattCharacteristic(
+            characteristicUuid,
+            properties,
+            permissions,
+        )
+        val gattCharacteristic = BluetoothGattCharacteristic(fwkGattCharacteristic)
+
+        Assert.assertEquals(fwkGattCharacteristic.uuid, gattCharacteristic.uuid)
+        Assert.assertEquals(fwkGattCharacteristic.properties, gattCharacteristic.properties)
+        Assert.assertEquals(fwkGattCharacteristic.permissions, gattCharacteristic.permissions)
+    }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattServiceTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattServiceTest.kt
new file mode 100644
index 0000000..0ac236b
--- /dev/null
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothGattServiceTest.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.bluetooth
+
+import android.bluetooth.BluetoothGattService as FwkBluetoothGattService
+import android.bluetooth.BluetoothGattCharacteristic as FwkBluetoothGattCharacteristic
+import android.bluetooth.BluetoothGattService.SERVICE_TYPE_PRIMARY
+import java.util.UUID
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BluetoothGattServiceTest {
+
+    @Test
+    fun constructorWithFwkInstance() {
+        val serviceUuid = UUID.randomUUID()
+        val fwkBluetoothGattService = FwkBluetoothGattService(serviceUuid, SERVICE_TYPE_PRIMARY)
+
+        val charUuid1 = UUID.randomUUID()
+        val fwkCharacteristic1 = FwkBluetoothGattCharacteristic(charUuid1, 0, 0)
+        fwkBluetoothGattService.addCharacteristic(fwkCharacteristic1)
+
+        val charUuid2 = UUID.randomUUID()
+        val fwkCharacteristic2 = FwkBluetoothGattCharacteristic(charUuid2, 0, 0)
+        fwkBluetoothGattService.addCharacteristic(fwkCharacteristic2)
+
+        val gattService = BluetoothGattService(fwkBluetoothGattService)
+
+        assertEquals(fwkBluetoothGattService.uuid, gattService.uuid)
+        assertEquals(2, gattService.characteristics.size)
+        assertEquals(charUuid1, gattService.characteristics[0].uuid)
+        assertEquals(charUuid2, gattService.characteristics[1].uuid)
+    }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt
new file mode 100644
index 0000000..cd842d8
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.bluetooth
+
+import android.bluetooth.BluetoothGattCharacteristic as FwkBluetoothGattCharacteristic
+import androidx.annotation.RestrictTo
+import java.util.UUID
+
+/**
+ * Represents a Bluetooth characteristic.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class BluetoothGattCharacteristic internal constructor(
+    internal var characteristic: FwkBluetoothGattCharacteristic
+) {
+    val uuid: UUID
+        get() = characteristic.uuid
+    val properties: Int
+        get() = characteristic.properties
+    val permissions: Int
+        get() = characteristic.permissions
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.kt
new file mode 100644
index 0000000..f5bfcbb
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.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.bluetooth
+
+import android.bluetooth.BluetoothGattService as FwkBluetoothGattService
+import androidx.annotation.RestrictTo
+import java.util.UUID
+
+/**
+ * Represents a Bluetooth GATT service.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class BluetoothGattService internal constructor(internal var fwkService: FwkBluetoothGattService) {
+    val uuid: UUID
+        get() = fwkService.uuid
+    val characteristics: List<BluetoothGattCharacteristic> =
+        fwkService.characteristics.map { BluetoothGattCharacteristic(it) }
+}
diff --git a/bluetooth/integration-tests/testapp/build.gradle b/bluetooth/integration-tests/testapp/build.gradle
index ba207e7..55523d6 100644
--- a/bluetooth/integration-tests/testapp/build.gradle
+++ b/bluetooth/integration-tests/testapp/build.gradle
@@ -46,7 +46,7 @@
     implementation(libs.kotlinStdlib)
     implementation(project(":bluetooth:bluetooth"))
 
-    implementation("androidx.activity:activity-ktx:1.7.0")
+    implementation("androidx.activity:activity-ktx:1.7.1")
     implementation("androidx.appcompat:appcompat:1.6.1")
     implementation(libs.constraintLayout)
     implementation("androidx.core:core-ktx:1.10.0")
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
index d51a375..219af7b 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
@@ -110,13 +110,14 @@
         fun getServices(): List<BluetoothGattService>
         fun getService(uuid: UUID): BluetoothGattService?
 
-        suspend fun read(characteristic: BluetoothGattCharacteristic): Result<ByteArray>
-        suspend fun write(
+        suspend fun readCharacteristic(characteristic: BluetoothGattCharacteristic):
+            Result<ByteArray>
+        suspend fun writeCharacteristic(
             characteristic: BluetoothGattCharacteristic,
             value: ByteArray,
             writeType: Int
         ): Result<Unit>
-        fun subscribeCharacteristic(characteristic: BluetoothGattCharacteristic): Flow<ByteArray>
+        fun subscribeToCharacteristic(characteristic: BluetoothGattCharacteristic): Flow<ByteArray>
         suspend fun awaitClose(onClosed: () -> Unit)
     }
 
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt
index 315fe6d..b305e73 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt
@@ -22,16 +22,23 @@
 import android.bluetooth.BluetoothGatt.GATT_SUCCESS
 import android.bluetooth.BluetoothGattCallback
 import android.bluetooth.BluetoothGattCharacteristic
+import android.bluetooth.BluetoothGattDescriptor
 import android.bluetooth.BluetoothGattService
 import android.content.Context
 import android.util.Log
+import androidx.collection.arrayMapOf
 import java.util.UUID
+import kotlin.coroutines.cancellation.CancellationException
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.consumeEach
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.job
 import kotlinx.coroutines.launch
 
@@ -39,7 +46,9 @@
     companion object {
         private const val TAG = "GattClientImpl"
         private const val GATT_MAX_MTU = 517
+        private val CCCD_UID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
     }
+
     private data class ClientTask(
         val taskBlock: () -> Unit
     ) {
@@ -48,18 +57,25 @@
     }
 
     private sealed interface ClientCallback {
-        val characteristic: BluetoothGattCharacteristic
-
-        class OnRead(
-            override val characteristic: BluetoothGattCharacteristic,
+        class OnCharacteristicRead(
+            val characteristic: BluetoothGattCharacteristic,
             val value: ByteArray,
             val status: Int
         ) : ClientCallback
 
-        class OnWrite(
-            override val characteristic: BluetoothGattCharacteristic,
+        class OnCharacteristicWrite(
+            val characteristic: BluetoothGattCharacteristic,
             val status: Int
         ) : ClientCallback
+
+        class OnDescriptorWrite(
+            val descriptor: BluetoothGattDescriptor,
+            val status: Int
+        ) : ClientCallback
+    }
+
+    private interface SubscribeListener {
+        fun onCharacteristicNotification(value: ByteArray)
     }
 
     @SuppressLint("MissingPermission")
@@ -71,6 +87,7 @@
         val connectResult = CompletableDeferred<Boolean>(parent = coroutineContext.job)
         val finished = Job(parent = coroutineContext.job)
         var currentTask: ClientTask? = null
+        val subscribeMap: MutableMap<BluetoothGattCharacteristic, SubscribeListener> = arrayMapOf()
 
         val callback = object : BluetoothGattCallback() {
             override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
@@ -104,7 +121,7 @@
                 status: Int
             ) {
                 currentTask?.callbackChannel?.trySend(
-                    ClientCallback.OnRead(characteristic, value, status))
+                    ClientCallback.OnCharacteristicRead(characteristic, value, status))
             }
 
             override fun onCharacteristicWrite(
@@ -113,14 +130,33 @@
                 status: Int
             ) {
                 currentTask?.callbackChannel?.trySend(
-                    ClientCallback.OnWrite(characteristic, status))
+                    ClientCallback.OnCharacteristicWrite(characteristic, status))
+            }
+
+            override fun onDescriptorWrite(
+                gatt: BluetoothGatt,
+                descriptor: BluetoothGattDescriptor,
+                status: Int
+            ) {
+                currentTask?.callbackChannel?.trySend(
+                    ClientCallback.OnDescriptorWrite(descriptor, status))
+            }
+
+            override fun onCharacteristicChanged(
+                gatt: BluetoothGatt,
+                characteristic: BluetoothGattCharacteristic,
+                value: ByteArray
+            ) {
+                synchronized(subscribeMap) {
+                    subscribeMap[characteristic]?.onCharacteristicNotification(value)
+                }
             }
         }
         val bluetoothGatt = device.connectGatt(context, /*autoConnect=*/false, callback)
         val tasks: Channel<ClientTask> = Channel(10)
 
         if (!connectResult.await()) {
-            Log.d(TAG, "Failed to connect to the remote GATT server")
+            Log.w(TAG, "Failed to connect to the remote GATT server")
             return@coroutineScope
         }
         val gattScope = object : BluetoothLe.GattClientScope {
@@ -147,7 +183,7 @@
                 return bluetoothGatt.getService(uuid)
             }
 
-            override suspend fun read(characteristic: BluetoothGattCharacteristic):
+            override suspend fun readCharacteristic(characteristic: BluetoothGattCharacteristic):
                 Result<ByteArray> {
                 val task = ClientTask {
                     bluetoothGatt.readCharacteristic(characteristic)
@@ -155,7 +191,7 @@
                 tasks.send(task)
                 while (true) {
                     val res = task.callbackChannel.receive()
-                    if (res !is ClientCallback.OnRead) continue
+                    if (res !is ClientCallback.OnCharacteristicRead) continue
                     if (res.characteristic != characteristic) continue
 
                     task.finished.complete(res.status == GATT_SUCCESS)
@@ -164,8 +200,7 @@
                 }
             }
 
-            @Suppress("ClassVerificationFailure")
-            override suspend fun write(
+            override suspend fun writeCharacteristic(
                 characteristic: BluetoothGattCharacteristic,
                 value: ByteArray,
                 writeType: Int
@@ -176,7 +211,7 @@
                 tasks.send(task)
                 while (true) {
                     val res = task.callbackChannel.receive()
-                    if (res !is ClientCallback.OnWrite) continue
+                    if (res !is ClientCallback.OnCharacteristicWrite) continue
                     if (res.characteristic.uuid != characteristic.uuid) continue
 
                     task.finished.complete(res.status == GATT_SUCCESS)
@@ -185,9 +220,50 @@
                 }
             }
 
-            override fun subscribeCharacteristic(characteristic: BluetoothGattCharacteristic):
+            override fun subscribeToCharacteristic(characteristic: BluetoothGattCharacteristic):
                 Flow<ByteArray> {
-                TODO("Not yet implemented")
+                val cccd = characteristic.getDescriptor(CCCD_UID) ?: return emptyFlow()
+
+                return callbackFlow {
+                    val listener = object : SubscribeListener {
+                        override fun onCharacteristicNotification(value: ByteArray) {
+                            trySend(value)
+                        }
+                    }
+                    if (!registerSubscribeListener(characteristic, listener)) {
+                        cancel(CancellationException("already subscribed"))
+                    }
+
+                    val task = ClientTask {
+                        bluetoothGatt.setCharacteristicNotification(characteristic, /*enable=*/true)
+                        bluetoothGatt.writeDescriptor(
+                            cccd,
+                            BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
+                        )
+                    }
+                    tasks.send(task)
+                    while (true) {
+                        val res = task.callbackChannel.receive()
+                        if (res !is ClientCallback.OnDescriptorWrite) continue
+                        if (res.descriptor != cccd) continue
+
+                        task.finished.complete(res.status == GATT_SUCCESS)
+                        if (res.status != GATT_SUCCESS) {
+                            cancel(CancellationException("failed to set notification"))
+                        }
+                        break
+                    }
+
+                    this.awaitClose {
+                        unregisterSubscribeListener(characteristic)
+                        bluetoothGatt.setCharacteristicNotification(characteristic,
+                            /*enable=*/false)
+                        bluetoothGatt.writeDescriptor(
+                            cccd,
+                            BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
+                        )
+                    }
+                }
             }
 
             override suspend fun awaitClose(onClosed: () -> Unit) {
@@ -198,6 +274,25 @@
                     onClosed()
                 }
             }
+
+            private fun registerSubscribeListener(
+                characteristic: BluetoothGattCharacteristic,
+                callback: SubscribeListener
+            ): Boolean {
+                synchronized(subscribeMap) {
+                    if (subscribeMap.containsKey(characteristic)) {
+                        return false
+                    }
+                    subscribeMap[characteristic] = callback
+                    return true
+                }
+            }
+
+            private fun unregisterSubscribeListener(characteristic: BluetoothGattCharacteristic) {
+                synchronized(subscribeMap) {
+                    subscribeMap.remove(characteristic)
+                }
+            }
         }
         coroutineScope {
             launch {
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
index 73c1935..e5f71ad 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
@@ -30,6 +30,7 @@
 import androidx.bluetooth.BluetoothLe
 import androidx.bluetooth.integration.testapp.R
 import androidx.bluetooth.integration.testapp.databinding.FragmentAdvertiserBinding
+import androidx.bluetooth.integration.testapp.ui.common.getColor
 import androidx.bluetooth.integration.testapp.ui.common.setViewEditText
 import androidx.bluetooth.integration.testapp.ui.common.toast
 import androidx.core.view.isVisible
@@ -63,12 +64,19 @@
             field = value
             if (value) {
                 _binding?.buttonAdvertise?.text = getString(R.string.stop_advertising)
+                _binding?.buttonAdvertise?.backgroundTintList = getColor(R.color.red_500)
             } else {
                 _binding?.buttonAdvertise?.text = getString(R.string.start_advertising)
+                _binding?.buttonAdvertise?.backgroundTintList = getColor(R.color.indigo_500)
                 advertiseJob?.cancel()
                 advertiseJob = null
             }
-            _binding?.viewOverlay?.isVisible = value
+            _binding?.textInputEditTextDisplayName?.isEnabled = !value
+            _binding?.checkBoxIncludeDeviceName?.isEnabled = !value
+            _binding?.checkBoxConnectable?.isEnabled = !value
+            _binding?.checkBoxDiscoverable?.isEnabled = !value
+            _binding?.buttonAddData?.isEnabled = !value
+            _binding?.viewRecyclerViewOverlay?.isVisible = value
         }
 
     private var _binding: FragmentAdvertiserBinding? = null
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
index 395bd0d7..aaa4232 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
@@ -16,9 +16,15 @@
 
 package androidx.bluetooth.integration.testapp.ui.common
 
+import android.content.res.ColorStateList
 import android.widget.Toast
+import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 
+fun Fragment.getColor(color: Int): ColorStateList? {
+    return ContextCompat.getColorStateList(requireContext(), color)
+}
+
 fun Fragment.toast(msg: String): Toast {
     return Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT)
 }
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 37c853b..593f46b 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
@@ -17,6 +17,7 @@
 package androidx.bluetooth.integration.testapp.ui.home
 
 import android.annotation.SuppressLint
+import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY
 import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ
 import android.bluetooth.le.AdvertiseData
 import android.bluetooth.le.AdvertiseSettings
@@ -39,6 +40,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.joinAll
 import kotlinx.coroutines.launch
 
@@ -150,14 +152,19 @@
                     val jobs = ArrayList<Job>()
                     for (srv in getServices()) {
                         for (char in srv.characteristics) {
-                            Log.d(TAG, "trying to read characteristic ${char.uuid}")
                             if (char.properties.and(PROPERTY_READ) == 0) continue
                             jobs.add(launch {
-                                val value = read(char).getOrNull()
+                                val value = readCharacteristic(char).getOrNull()
                                 if (value != null) {
                                     Log.d(TAG, "Successfully read characteristic value=$value")
                                 }
                             })
+                            if (char.properties.and(PROPERTY_NOTIFY) != 0) {
+                                jobs.add(launch {
+                                    val value = subscribeToCharacteristic(char).first()
+                                    Log.d(TAG, "Successfully get characteristic value=$value")
+                                })
+                            }
                         }
                     }
                     jobs.joinAll()
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.kt
new file mode 100644
index 0000000..8e5ab73
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.bluetooth.integration.testapp.ui.scanner
+
+// TODO(ofy) Migrate to androidx.bluetooth.BluetoothGattService once in place
+import android.bluetooth.BluetoothGattService
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.bluetooth.integration.testapp.R
+import androidx.recyclerview.widget.RecyclerView
+
+class DeviceServicesAdapter(var services: List<BluetoothGattService>) :
+    RecyclerView.Adapter<DeviceServicesAdapter.ViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.item_device_service, parent, false)
+        return ViewHolder(view)
+    }
+
+    override fun getItemCount(): Int {
+        return services.size
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val service = services[position]
+        holder.bind(service)
+    }
+
+    inner class ViewHolder(itemView: View) :
+        RecyclerView.ViewHolder(itemView) {
+
+        private val textViewUuid: TextView = itemView.findViewById(R.id.text_view_uuid)
+
+        fun bind(service: BluetoothGattService) {
+            textViewUuid.text = service.uuid.toString()
+        }
+    }
+}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
index c227ab5..7641f8a 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
@@ -25,6 +25,7 @@
 import android.widget.TextView
 
 import androidx.bluetooth.integration.testapp.R
+import androidx.core.view.isVisible
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.RecyclerView
@@ -62,8 +63,9 @@
         @SuppressLint("MissingPermission")
         fun bind(scanResult: ScanResult) {
             currentScanResult = scanResult
-            textViewDeviceName.text = scanResult.device.name
             textViewDeviceAddress.text = scanResult.device.address
+            textViewDeviceName.text = scanResult.device.name
+            textViewDeviceName.isVisible = scanResult.device.name.isNullOrEmpty().not()
         }
     }
 }
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 7411f0a..76d6330 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
@@ -23,26 +23,36 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.TextView
 
 import androidx.bluetooth.integration.testapp.R
 import androidx.bluetooth.integration.testapp.databinding.FragmentScannerBinding
+import android.annotation.SuppressLint
+// TODO(ofy) Migrate to androidx.bluetooth.BluetoothDevice once in place
+// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
+import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
+import androidx.bluetooth.integration.testapp.ui.common.getColor
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.DividerItemDecoration
 import androidx.recyclerview.widget.LinearLayoutManager
 
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
-import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.tabs.TabLayout
+import com.google.android.material.tabs.TabLayout.Tab
 
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
+import java.lang.Exception
 
 class ScannerFragment : Fragment() {
 
     private companion object {
         private const val TAG = "ScannerFragment"
+
+        private const val TAB_RESULTS_POSITION = 0
     }
 
     private lateinit var scannerViewModel: ScannerViewModel
@@ -52,20 +62,50 @@
 
     private var scannerAdapter: ScannerAdapter? = null
 
+    private var deviceServicesAdapter: DeviceServicesAdapter? = null
+
     private val scanScope = CoroutineScope(Dispatchers.Main + Job())
     private var scanJob: Job? = null
 
+    private val connectScope = CoroutineScope(Dispatchers.Default + Job())
+    private var connectJob: Job? = null
+
     private var isScanning: Boolean = false
         set(value) {
             field = value
             if (value) {
                 _binding?.buttonScan?.text = getString(R.string.stop_scanning)
+                _binding?.buttonScan?.backgroundTintList = getColor(R.color.red_500)
             } else {
                 _binding?.buttonScan?.text = getString(R.string.start_scanning)
+                _binding?.buttonScan?.backgroundTintList = getColor(R.color.indigo_500)
                 scanJob?.cancel()
+                scanJob = null
             }
         }
 
+    private var showingScanResults: Boolean = false
+        set(value) {
+            field = value
+            _binding?.relativeLayoutScanResults?.isVisible = value
+            _binding?.linearLayoutDevice?.isVisible = !value
+        }
+
+    private val onTabSelectedListener = object : TabLayout.OnTabSelectedListener {
+        override fun onTabSelected(tab: Tab) {
+            showingScanResults = tab.position == TAB_RESULTS_POSITION
+            if (tab.position != TAB_RESULTS_POSITION) {
+                updateDeviceUI(scannerViewModel.deviceConnection(tab.position))
+            }
+        }
+
+        override fun onTabUnselected(tab: Tab) {
+        }
+
+        override fun onTabReselected(tab: Tab) {
+        }
+    }
+
     private var _binding: FragmentScannerBinding? = null
 
     // This property is only valid between onCreateView and onDestroyView.
@@ -82,12 +122,20 @@
 
         _binding = FragmentScannerBinding.inflate(inflater, container, false)
 
+        binding.tabLayout.addOnTabSelectedListener(onTabSelectedListener)
+
         scannerAdapter = ScannerAdapter { scanResult -> onClickScanResult(scanResult) }
         binding.recyclerViewScanResults.adapter = scannerAdapter
         binding.recyclerViewScanResults.addItemDecoration(
             DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
         )
 
+        deviceServicesAdapter = DeviceServicesAdapter(emptyList())
+        binding.recyclerViewDeviceServices.adapter = deviceServicesAdapter
+        binding.recyclerViewDeviceServices.addItemDecoration(
+            DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
+        )
+
         initData()
 
         binding.buttonScan.setOnClickListener {
@@ -105,11 +153,15 @@
         super.onDestroyView()
         _binding = null
         isScanning = false
+        scanJob?.cancel()
+        scanJob = null
     }
 
     private fun initData() {
         scannerAdapter?.submitList(scannerViewModel.results)
         scannerAdapter?.notifyItemRangeChanged(0, scannerViewModel.results.size)
+
+        scannerViewModel.devices.map { it.scanResult }.forEach(::addNewTab)
     }
 
     private fun startScan() {
@@ -133,6 +185,96 @@
     }
 
     private fun onClickScanResult(scanResult: ScanResult) {
-        Log.d(TAG, "onClickScanResult() called with: scanResult = $scanResult")
+        isScanning = false
+
+        val index = scannerViewModel.addDeviceConnectionIfNew(scanResult)
+
+        val deviceTab = if (index == ScannerViewModel.NEW_DEVICE) {
+            addNewTab(scanResult)
+        } else {
+            binding.tabLayout.getTabAt(index)
+        }
+
+        // To prevent TabSelectedListener being triggered when a tab is promatically selected.
+        binding.tabLayout.removeOnTabSelectedListener(onTabSelectedListener)
+        binding.tabLayout.selectTab(deviceTab)
+        binding.tabLayout.addOnTabSelectedListener(onTabSelectedListener)
+
+        showingScanResults = false
+
+        connectTo(scannerViewModel.deviceConnection(binding.tabLayout.selectedTabPosition))
+    }
+
+    @SuppressLint("MissingPermission")
+    private fun addNewTab(scanResult: ScanResult): Tab {
+        val deviceAddress = scanResult.device.address
+        val deviceName = scanResult.device.name
+
+        val newTab = binding.tabLayout.newTab()
+        newTab.setCustomView(R.layout.tab_item_device)
+
+        val customView = newTab.customView
+        customView?.findViewById<TextView>(R.id.text_view_address)?.text = deviceAddress
+        customView?.findViewById<TextView>(R.id.text_view_name)?.text = deviceName
+
+        binding.tabLayout.addTab(newTab)
+        return newTab
+    }
+
+    private fun connectTo(deviceConnection: DeviceConnection) {
+        Log.d(TAG, "connectTo() called with: deviceConnection = $deviceConnection")
+
+        connectJob = connectScope.launch {
+            deviceConnection.status = Status.CONNECTING
+            launch(Dispatchers.Main) {
+                updateDeviceUI(deviceConnection)
+            }
+
+            try {
+                bluetoothLe.connectGatt(requireContext(), deviceConnection.scanResult.device) {
+                    Log.d(TAG, "connectGatt result. getServices() = ${getServices()}")
+
+                    deviceConnection.status = Status.CONNECTED
+                    deviceConnection.services = getServices()
+                    launch(Dispatchers.Main) {
+                        updateDeviceUI(deviceConnection)
+                    }
+                }
+            } catch (exception: Exception) {
+                Log.e(TAG, "connectTo: exception", exception)
+
+                deviceConnection.status = Status.CONNECTION_FAILED
+                launch(Dispatchers.Main) {
+                    updateDeviceUI(deviceConnection)
+                }
+            }
+        }
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    private fun updateDeviceUI(deviceConnection: DeviceConnection) {
+        binding.progressIndicatorDeviceConnection.isVisible = false
+
+        when (deviceConnection.status) {
+            Status.NOT_CONNECTED -> {
+                binding.textViewDeviceConnectionStatus.text = getString(R.string.not_connected)
+                binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.green_500))
+            }
+            Status.CONNECTING -> {
+                binding.progressIndicatorDeviceConnection.isVisible = true
+                binding.textViewDeviceConnectionStatus.text = getString(R.string.connecting)
+                binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.indigo_500))
+            }
+            Status.CONNECTED -> {
+                binding.textViewDeviceConnectionStatus.text = getString(R.string.connected)
+                binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.indigo_500))
+            }
+            Status.CONNECTION_FAILED -> {
+                binding.textViewDeviceConnectionStatus.text = getString(R.string.connection_failed)
+                binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.red_500))
+            }
+        }
+        deviceServicesAdapter?.services = deviceConnection.services
+        deviceServicesAdapter?.notifyDataSetChanged()
     }
 }
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
index 7d213fd..518fe42 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
@@ -16,24 +16,60 @@
 
 package androidx.bluetooth.integration.testapp.ui.scanner
 
+// TODO(ofy) Migrate to androidx.bluetooth.ScanResult once in place
+import android.bluetooth.BluetoothGattService
 import android.bluetooth.le.ScanResult
 import androidx.lifecycle.ViewModel
 
 class ScannerViewModel : ViewModel() {
 
-    private companion object {
+    internal companion object {
         private const val TAG = "ScannerViewModel"
+
+        internal const val NEW_DEVICE = -1
     }
 
     internal val results: List<ScanResult> get() = _results.values.toList()
-
     private val _results = mutableMapOf<String, ScanResult>()
 
+    internal val devices: Set<DeviceConnection> get() = _devices
+    private val _devices = mutableSetOf<DeviceConnection>()
+
     fun addScanResultIfNew(scanResult: ScanResult): Boolean {
         val deviceAddress = scanResult.device.address
 
-        if (_results.containsKey(deviceAddress)) return false
+        if (_results.containsKey(deviceAddress)) {
+            return false
+        }
+
         _results[deviceAddress] = scanResult
         return true
     }
+
+    fun addDeviceConnectionIfNew(scanResult: ScanResult): Int {
+        val deviceConnection = DeviceConnection(scanResult)
+
+        val indexOf = _devices.map { it.scanResult }.indexOf(scanResult)
+        if (indexOf != -1) {
+            // Index 0 is Results page; Tabs for devices start from 1.
+            return indexOf + 1
+        }
+
+        _devices.add(deviceConnection)
+        return NEW_DEVICE
+    }
+
+    fun deviceConnection(position: Int): DeviceConnection {
+        // Index 0 is Results page; Tabs for devices start from 1.
+        return devices.elementAt(position - 1)
+    }
+}
+
+class DeviceConnection(val scanResult: ScanResult) {
+    var status = Status.NOT_CONNECTED
+    var services = emptyList<BluetoothGattService>()
+}
+
+enum class Status {
+    NOT_CONNECTED, CONNECTING, CONNECTED, CONNECTION_FAILED
 }
diff --git a/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml b/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml
new file mode 100644
index 0000000..89633bb
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
index c6e37d3..014dc75 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
@@ -88,12 +88,12 @@
 
     <Button
         android:id="@+id/button_add_data"
-        style="@style/Widget.AppCompat.Button.Borderless"
+        style="@style/Widget.MaterialComponents.Button.Icon"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginStart="8dp"
         android:text="@string/add_data"
-        android:textColor="@color/purple_500"
+        app:icon="@drawable/baseline_add_24"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/text_view_advertising_data" />
 
@@ -108,12 +108,13 @@
         tools:listitem="@layout/item_advertiser_data" />
 
     <View
-        android:id="@+id/view_overlay"
+        android:id="@+id/view_recycler_view_overlay"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/black_alpha_50"
+        android:layout_height="0dp"
         android:clickable="true"
-        android:visibility="gone" />
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/button_add_data" />
 
     <Button
         android:id="@+id/button_advertise"
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
index e27e808..9cf7c3484 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
@@ -21,21 +21,97 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/recycler_view_scan_results"
+    <com.google.android.material.tabs.TabLayout
+        android:id="@+id/tab_layout"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        app:layoutManager="LinearLayoutManager"
-        tools:itemCount="3"
-        tools:listitem="@layout/item_scan_result" />
-
-    <Button
-        android:id="@+id/button_scan"
-        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_margin="16dp"
-        android:text="@string/start_scanning"
+        app:layout_constraintTop_toTopOf="parent"
+        app:tabGravity="start"
+        app:tabMode="scrollable">
+
+        <com.google.android.material.tabs.TabItem
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/scan_results" />
+
+    </com.google.android.material.tabs.TabLayout>
+
+    <RelativeLayout
+        android:id="@+id/relative_layout_scan_results"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent" />
+        app:layout_constraintTop_toBottomOf="@+id/tab_layout"
+        tools:visibility="gone">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/recycler_view_scan_results"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            tools:itemCount="3"
+            tools:listitem="@layout/item_scan_result" />
+
+        <Button
+            android:id="@+id/button_scan"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentBottom="true"
+            android:layout_margin="16dp"
+            android:text="@string/start_scanning" />
+
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/linear_layout_device"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:orientation="vertical"
+        android:padding="16dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/tab_layout"
+        tools:visibility="visible">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical">
+
+            <TextView
+                android:id="@+id/text_view_device_connection_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:padding="8dp"
+                android:text="@string/not_connected"
+                android:textAllCaps="true"
+                android:textColor="@color/green_500"
+                android:textSize="16sp" />
+
+            <com.google.android.material.progressindicator.CircularProgressIndicator
+                android:id="@+id/progress_indicator_device_connection"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:indeterminate="true"
+                android:visibility="gone"
+                app:indicatorSize="24dp" />
+
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?android:attr/dividerVertical" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/recycler_view_device_services"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            tools:itemCount="3"
+            tools:listitem="@layout/item_device_service" />
+
+    </LinearLayout>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml
new file mode 100644
index 0000000..cb091804
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:padding="8dp">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/generic_attribute"
+        android:textColor="#000000" />
+
+    <TextView
+        android:id="@+id/text_view_uuid"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        tools:text="UUID: 0x1800" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/primary_service"
+        android:textAllCaps="true" />
+
+</LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
index 649c4e4..fa2a9bc 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
   Copyright 2023 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +15,7 @@
   limitations under the License.
   -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -38,24 +40,25 @@
         android:paddingEnd="8dp">
 
         <TextView
-            android:id="@+id/text_view_device_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textColor="#000000"
-            tools:text="Omer S23 Ultra" />
-
-        <TextView
             android:id="@+id/text_view_device_address"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:textColor="@color/black"
             tools:text="AB:CD:12:34:56:78" />
 
+        <TextView
+            android:id="@+id/text_view_device_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            tools:text="Omer S23 Ultra" />
+
     </LinearLayout>
 
     <Button
         android:id="@+id/button_connect"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/connect" />
+        android:text="@string/connect"
+        app:backgroundTint="@color/green_500" />
 
 </LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml
new file mode 100644
index 0000000..23880a4
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text_view_address"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/black"
+        tools:text="70:04:13:03:98:B9" />
+
+    <TextView
+        android:id="@+id/text_view_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        tools:text="R33-0473" />
+
+</LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml b/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
index a7e6ba3..2b48616 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
@@ -15,12 +15,10 @@
   limitations under the License.
   -->
 <resources>
-    <color name="purple_200">#FFBB86FC</color>
-    <color name="purple_500">#FF6200EE</color>
-    <color name="purple_700">#FF3700B3</color>
-    <color name="teal_200">#FF03DAC5</color>
-    <color name="teal_700">#FF018786</color>
+    <color name="white">#FFFFFF</color>
+    <color name="indigo_500">#3F51B5</color>
+    <color name="indigo_700">#303F9F</color>
+    <color name="green_500">#4CAF50</color>
+    <color name="red_500">#F44336</color>
     <color name="black">#FF000000</color>
-    <color name="white">#FFFFFFFF</color>
-    <color name="black_alpha_50">#80000000</color>
 </resources>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 5707c81..b6a131a 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -26,6 +26,13 @@
     <string name="stop_scanning">Stop scanning</string>
     <string name="scan_result_icon">Scan Result Icon</string>
     <string name="connect">Connect</string>
+    <string name="scan_results">Scan Results</string>
+    <string name="not_connected">Not Connected</string>
+    <string name="connecting">Connecting…</string>
+    <string name="connected">Connected</string>
+    <string name="connection_failed">Connection Failed</string>
+    <string name="generic_attribute">Generic Attribute</string>
+    <string name="primary_service">Primary Service</string>
 
     <!-- Advertiser -->
     <string name="configure_advertising_packet">Configure Advertising Packet</string>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml b/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
index 2373b57c..7ff92c0 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
@@ -14,19 +14,11 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<resources xmlns:tools="http://schemas.android.com/tools">
-    <!-- Base application theme. -->
-    <style name="Theme.TestApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
-        <!-- Primary brand color. -->
-        <item name="colorPrimary">@color/purple_500</item>
-        <item name="colorPrimaryVariant">@color/purple_700</item>
-        <item name="colorOnPrimary">@color/white</item>
-        <!-- Secondary brand color. -->
-        <item name="colorSecondary">@color/teal_200</item>
-        <item name="colorSecondaryVariant">@color/teal_700</item>
-        <item name="colorOnSecondary">@color/black</item>
-        <!-- Status bar color. -->
+<resources>
+    <style name="Theme.TestApp" parent="Theme.MaterialComponents.Light">
+        <item name="colorPrimary">@color/indigo_500</item>
+        <item name="colorPrimaryVariant">@color/indigo_700</item>
         <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
-        <!-- Customize your theme here. -->
+        <item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>
     </style>
 </resources>
diff --git a/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl b/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl
deleted file mode 100644
index 5ec5a66..0000000
--- a/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.net;
-
-@JavaOnlyStableParcelable parcelable Uri;
diff --git a/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl b/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index f6ed3764..1640305 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -80,12 +80,6 @@
 const val ENABLE_DOCUMENTATION = "androidx.enableDocumentation"
 
 /**
- * Adjusts the set of projects participating in this build.
- * See settings.gradle for more information
- */
-const val PROJECT_SUBSET = "androidx.projects"
-
-/**
  * Setting this property puts a summary of the relevant failure messages into standard error
  */
 const val SUMMARIZE_STANDARD_ERROR = "androidx.summarizeStderr"
@@ -167,7 +161,6 @@
     ENABLE_COMPOSE_COMPILER_REPORTS,
     DISPLAY_TEST_OUTPUT,
     ENABLE_DOCUMENTATION,
-    PROJECT_SUBSET,
     STUDIO_TYPE,
     SUMMARIZE_STANDARD_ERROR,
     USE_MAX_DEP_VERSIONS,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index d42591b..f868506 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -45,6 +45,7 @@
 import com.android.build.api.variant.ApplicationAndroidComponentsExtension
 import com.android.build.api.variant.HasAndroidTest
 import com.android.build.api.variant.LibraryAndroidComponentsExtension
+import com.android.build.api.variant.Variant
 import com.android.build.gradle.AppExtension
 import com.android.build.gradle.AppPlugin
 import com.android.build.gradle.BaseExtension
@@ -329,7 +330,10 @@
         }
 
         project.extensions.getByType<ApplicationAndroidComponentsExtension>().apply {
-            onVariants { it.configureTests() }
+            onVariants {
+                it.configureTests()
+                it.artRewritingWorkaround()
+            }
             finalizeDsl {
                 project.configureAndroidProjectForLint(
                     it.lint,
@@ -358,6 +362,14 @@
         excludeVersionFilesFromTestApks()
     }
 
+    private fun Variant.artRewritingWorkaround() {
+        // b/279234807
+        experimentalProperties.put(
+            "android.experimental.art-profile-r8-rewriting",
+            false
+        )
+    }
+
     private fun HasAndroidTest.configureLicensePackaging() {
         androidTest?.packaging?.resources?.apply {
             // Workaround a limitation in AGP that fails to merge these META-INF license files.
@@ -443,7 +455,10 @@
             beforeVariants(selector().withBuildType("release")) { variant ->
                 variant.enableUnitTest = false
             }
-            onVariants { it.configureTests() }
+            onVariants {
+                it.configureTests()
+                it.artRewritingWorkaround()
+            }
             finalizeDsl {
                 project.configureAndroidProjectForLint(it.lint, androidXExtension, isLibrary = true)
             }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt b/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
index 4b207e8..65f0428 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
@@ -45,13 +45,8 @@
  * in the build, to make project configuration run more quickly.
  */
 fun Project.getProjectSubset(): String? {
-    val prop = project.providers.gradleProperty("androidx.projects")
-    if (prop.isPresent()) {
-        return prop.get().uppercase()
-    }
-
     val envProp = project.providers.environmentVariable("ANDROIDX_PROJECTS")
-    if (envProp.isPresent()) {
+    if (envProp.isPresent) {
         return envProp.get().uppercase()
     }
     return null
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
index d9587ea..0f22c64 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
@@ -56,7 +56,9 @@
     // File where the library's public resources are recorded
     val resourceFile: File,
     // Directory where native API files are stored
-    val nativeApiDirectory: File
+    val nativeApiDirectory: File,
+    // Directory where the library's stable AIDL surface is recorded
+    val aidlApiDirectory: File
 ) : Serializable {
 
     /**
@@ -96,7 +98,8 @@
                 restrictedApiFile = File(apiFileDir, "$PREFIX_RESTRICTED$baseName$EXTENSION"),
                 experimentalApiFile = File(apiFileDir, "$PREFIX_EXPERIMENTAL$baseName$EXTENSION"),
                 resourceFile = File(apiFileDir, "$PREFIX_RESOURCE$baseName$EXTENSION"),
-                nativeApiDirectory = File(apiFileDir, NATIVE_API_DIRECTORY_NAME).resolve(baseName)
+                nativeApiDirectory = File(apiFileDir, NATIVE_API_DIRECTORY_NAME).resolve(baseName),
+                aidlApiDirectory = File(apiFileDir, AIDL_API_DIRECTORY_NAME).resolve(baseName)
             )
         }
 
@@ -134,6 +137,11 @@
          * Directory name for location of native API files
          */
         private const val NATIVE_API_DIRECTORY_NAME = "native"
+
+        /**
+         * Directory name for location of AIDL API files
+         */
+        private const val AIDL_API_DIRECTORY_NAME = "aidl"
     }
 }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
index 708cd58..84ef383 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
@@ -26,6 +26,7 @@
 import androidx.build.libabigail.NativeApiTasks
 import androidx.build.metalava.MetalavaTasks
 import androidx.build.resources.ResourceTasks
+import androidx.build.stableaidl.setupWithStableAidlPlugin
 import androidx.build.version
 import com.android.build.gradle.LibraryExtension
 import com.android.build.gradle.tasks.ProcessLibraryManifest
@@ -198,6 +199,8 @@
             )
         }
 
+        project.setupWithStableAidlPlugin()
+
         if (config is LibraryApiTaskConfig) {
             ResourceTasks.setupProject(
                 project, Release.DEFAULT_PUBLISH_CONFIG,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
index 0286efc..b900386 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
@@ -18,6 +18,9 @@
 
 import androidx.build.checkapi.ApiLocation
 import com.google.common.io.Files
+import java.io.File
+import java.security.MessageDigest
+import org.apache.commons.io.FileUtils
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.logging.Logger
@@ -25,14 +28,13 @@
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFiles
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
-import java.io.File
-import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.options.Option
 
 /**
@@ -189,6 +191,54 @@
     }
 }
 
+fun copyDir(
+    source: File,
+    dest: File,
+    permitOverwriting: Boolean,
+    logger: Logger
+) {
+    val sourceHash = if (source.exists()) {
+        hashDir(source)
+    } else {
+        null
+    }
+    val overwriting = dest.exists() && !sourceHash.contentEquals(hashDir(dest))
+    val changing = overwriting || (dest.exists() != source.exists())
+    if (changing) {
+        if (overwriting && !permitOverwriting) {
+            val message = "Modifying the API definition for a previously released artifact " +
+                "having a final API version (version not ending in '-alpha') is not " +
+                "allowed.\n\n" +
+                "Previously declared definition is $dest\n" +
+                "Current generated   definition is $source\n\n" +
+                "Did you mean to increment the library version first?\n\n" +
+                "If you have reason to overwrite the API files for the previous release " +
+                "anyway, you can run `./gradlew updateApi -Pforce` to ignore this message"
+            throw GradleException(message)
+        }
+        FileUtils.deleteDirectory(dest)
+        if (source.exists()) {
+            FileUtils.copyDirectory(source, dest)
+            logger.lifecycle("Copied $source to $dest")
+        } else {
+            logger.lifecycle("Deleted $dest because $source does not exist")
+        }
+    }
+}
+
+fun hashDir(dir: File): ByteArray {
+    val digest = MessageDigest.getInstance("SHA-256")
+    dir.listFiles()?.forEach { file ->
+        val fileBytes = if (file.isDirectory) {
+            hashDir(file)
+        } else {
+            file.readBytes()
+        }
+        digest.update(fileBytes)
+    }
+    return digest.digest()
+}
+
 /**
  * Returns -1 if [text] has fewer than [count] newline characters, 0 if equal, and 1 if greater
  * than.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt
new file mode 100644
index 0000000..f700913
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build.stableaidl
+
+import androidx.build.BUILD_ON_SERVER_TASK
+import androidx.build.getSupportRootFolder
+import androidx.stableaidl.withStableAidlPlugin
+import java.io.File
+import org.gradle.api.Project
+
+fun Project.setupWithStableAidlPlugin() = this.withStableAidlPlugin { ext ->
+    ext.checkAction.apply {
+        before(project.tasks.named("check"))
+        before(project.tasks.named(BUILD_ON_SERVER_TASK))
+        before(project.tasks.register("checkAidlApi") { task ->
+            task.group = "API"
+            task.description = "Checks that the API surface generated Stable AIDL sources " +
+                "matches the checked in API surface"
+        })
+    }
+
+    ext.updateAction.apply {
+        before(project.tasks.named("updateApi"))
+        before(project.tasks.register("updateAidlApi") { task ->
+            task.group = "API"
+            task.description = "Updates the checked in API surface based on Stable AIDL sources"
+        })
+    }
+
+    // Don't show tasks added by the Stable AIDL plugin.
+    ext.taskGroup = null
+
+    // Use a single top-level directory for shadow framework definitions.
+    ext.addStaticImportDirs(
+        File(project.getSupportRootFolder(), "buildSrc/stableAidlImports")
+    )
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
index 4b9fe1c..8e922b1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
@@ -22,8 +22,6 @@
 import androidx.build.getSdkPath
 import androidx.build.getSupportRootFolder
 import androidx.build.getVersionByName
-import androidx.build.hasSupportRootFolder
-import androidx.build.setSupportRootFolder
 import com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
 import java.io.File
 import java.nio.file.Files
@@ -125,6 +123,12 @@
     open val vmOptions = File(project.getSupportRootFolder(), "development/studio/studio.vmoptions")
 
     /**
+     * The path to the SDK directory used by Studio.
+     */
+    @get:Internal
+    open val localSdkPath = project.getSdkPath()
+
+    /**
      * List of additional environment variables to pass into the Studio application.
      */
     @get:Internal
@@ -179,18 +183,6 @@
      */
     private fun setupSymlinksIfNeeded() {
         val paths = listOf("system-images", "emulator")
-
-        // The support root folder may not have been set yet, in which case we'll optimistically
-        // assume that it matches the root project. This won't work for ui or Playground, but we'll
-        // fail gracefully later.
-        val hadSupportRootFolder = project.hasSupportRootFolder()
-        if (!hadSupportRootFolder) {
-            project.setSupportRootFolder(project.rootDir)
-        }
-        val localSdkPath = project.getSdkPath()
-        if (!hadSupportRootFolder) {
-            project.setSupportRootFolder(null)
-        }
         if (!localSdkPath.exists()) {
             // We probably got the support root folder wrong. Fail gracefully.
             return
@@ -229,11 +221,15 @@
      */
     private fun launch() {
         if (checkLicenseAgreement(services)) {
-            if (requiresProjectList && !System.getenv().containsKey("ANDROIDX_PROJECTS")) {
+            if (requiresProjectList &&
+                !System.getenv().containsKey("ANDROIDX_PROJECTS") &&
+                !System.getenv().containsKey("PROJECT_PREFIX")
+                ) {
                 throw GradleException(
                     """
                     Please specify which set of projects you'd like to open in studio
                     with ANDROIDX_PROJECTS=MAIN ./gradlew studio
+                    or PROJECT_PREFIX=:room: ./gradlew studio
 
                     For possible options see settings.gradle
                     """.trimIndent()
diff --git a/core/core/src/main/stableAidlImports/android/app/Notification.aidl b/buildSrc/stableAidlImports/android/app/Notification.aidl
similarity index 100%
rename from core/core/src/main/stableAidlImports/android/app/Notification.aidl
rename to buildSrc/stableAidlImports/android/app/Notification.aidl
diff --git a/media/media/src/main/stableAidlImports/android/app/PendingIntent.aidl b/buildSrc/stableAidlImports/android/app/PendingIntent.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/app/PendingIntent.aidl
rename to buildSrc/stableAidlImports/android/app/PendingIntent.aidl
diff --git a/browser/browser/src/main/stableAidlImports/android/content/ComponentName.aidl b/buildSrc/stableAidlImports/android/content/ComponentName.aidl
similarity index 100%
rename from browser/browser/src/main/stableAidlImports/android/content/ComponentName.aidl
rename to buildSrc/stableAidlImports/android/content/ComponentName.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/content/Intent.aidl b/buildSrc/stableAidlImports/android/content/Intent.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/content/Intent.aidl
rename to buildSrc/stableAidlImports/android/content/Intent.aidl
diff --git a/javascriptengine/javascriptengine/src/main/stableAidlImports/android/content/res/AssetFileDescriptor.aidl b/buildSrc/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
similarity index 100%
rename from javascriptengine/javascriptengine/src/main/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
rename to buildSrc/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/content/res/Configuration.aidl b/buildSrc/stableAidlImports/android/content/res/Configuration.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/content/res/Configuration.aidl
rename to buildSrc/stableAidlImports/android/content/res/Configuration.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/graphics/Insets.aidl b/buildSrc/stableAidlImports/android/graphics/Insets.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/graphics/Insets.aidl
rename to buildSrc/stableAidlImports/android/graphics/Insets.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/graphics/Rect.aidl b/buildSrc/stableAidlImports/android/graphics/Rect.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/graphics/Rect.aidl
rename to buildSrc/stableAidlImports/android/graphics/Rect.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/location/Location.aidl b/buildSrc/stableAidlImports/android/location/Location.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/location/Location.aidl
rename to buildSrc/stableAidlImports/android/location/Location.aidl
diff --git a/media/media/src/main/stableAidlImports/android/net/Uri.aidl b/buildSrc/stableAidlImports/android/net/Uri.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/net/Uri.aidl
rename to buildSrc/stableAidlImports/android/net/Uri.aidl
diff --git a/core/core/src/main/stableAidlImports/android/os/Bundle.aidl b/buildSrc/stableAidlImports/android/os/Bundle.aidl
similarity index 100%
rename from core/core/src/main/stableAidlImports/android/os/Bundle.aidl
rename to buildSrc/stableAidlImports/android/os/Bundle.aidl
diff --git a/car/app/app-projected/src/main/stableAidlImports/android/os/IBinder.aidl b/buildSrc/stableAidlImports/android/os/IBinder.aidl
similarity index 100%
rename from car/app/app-projected/src/main/stableAidlImports/android/os/IBinder.aidl
rename to buildSrc/stableAidlImports/android/os/IBinder.aidl
diff --git a/media/media/src/main/stableAidlImports/android/view/KeyEvent.aidl b/buildSrc/stableAidlImports/android/view/KeyEvent.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/view/KeyEvent.aidl
rename to buildSrc/stableAidlImports/android/view/KeyEvent.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/MotionEvent.aidl b/buildSrc/stableAidlImports/android/view/MotionEvent.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/MotionEvent.aidl
rename to buildSrc/stableAidlImports/android/view/MotionEvent.aidl
diff --git a/media2/media2-session/src/main/stableAidlImports/android/view/Surface.aidl b/buildSrc/stableAidlImports/android/view/Surface.aidl
similarity index 100%
rename from media2/media2-session/src/main/stableAidlImports/android/view/Surface.aidl
rename to buildSrc/stableAidlImports/android/view/Surface.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/EditorInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedText.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
diff --git a/busytown/androidx_multiplatform.sh b/busytown/androidx_multiplatform.sh
new file mode 120000
index 0000000..e2a6ac8
--- /dev/null
+++ b/busytown/androidx_multiplatform.sh
@@ -0,0 +1 @@
+./androidx_compose_multiplatform.sh
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
index 3ce18a3..a072374 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
@@ -296,9 +296,6 @@
         val action = FocusMeteringAction.Builder(factory.createPoint(0f, 0f)).build()
         bindUseCase(imageAnalysis)
 
-        // TODO(b/269968191): wait till camera is ready for submitting requests
-        waitForResult(1).verify({ _, _ -> true }, TIMEOUT)
-
         // Act.
         cameraControl.startFocusAndMetering(action).await()
 
@@ -345,9 +342,6 @@
         val action = FocusMeteringAction.Builder(factory.createPoint(0f, 0f)).build()
         bindUseCase(imageAnalysis)
 
-        // TODO(b/269968191): wait till camera is ready for submitting requests
-        waitForResult(1).verify({ _, _ -> true }, TIMEOUT)
-
         // Act.
         cameraControl.startFocusAndMetering(action).await()
         cameraControl.cancelFocusAndMetering().await()
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
index d5795f9..5ed3f28 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
@@ -19,6 +19,7 @@
 package androidx.camera.camera2.pipe.testing
 
 import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraDevice
 import android.hardware.camera2.CaptureRequest
 import androidx.annotation.RequiresApi
@@ -30,7 +31,10 @@
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
 import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpInactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
 import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
@@ -68,10 +72,22 @@
         NoOpInactiveSurfaceCloser,
     ),
 ) : UseCaseCamera {
+    val cameraMetadata =
+        cameraPipe.cameras().awaitCameraMetadata(CameraId.fromCamera2Id(cameraId))!!
+    val streamConfigurationMap =
+        cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
+    val cameraQuirks = CameraQuirks(
+        cameraMetadata,
+        StreamConfigurationMapCompat(
+            streamConfigurationMap,
+            OutputSizesCorrector(cameraMetadata, streamConfigurationMap)
+        )
+    )
     val useCaseCameraGraphConfig =
         UseCaseCameraConfig(
             useCases,
             CameraStateAdapter(),
+            cameraQuirks,
             CameraGraph.Flags()
         ).provideUseCaseGraphConfig(
             callbackMap = callbackMap,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index d80d27a..71ea477 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -62,9 +62,18 @@
         if (CameraNoResponseWhenEnablingFlashQuirk.isEnabled(cameraMetadata)) {
             quirks.add(CameraNoResponseWhenEnablingFlashQuirk())
         }
+        if (CaptureSessionStuckQuirk.isEnabled()) {
+            quirks.add(CaptureSessionStuckQuirk())
+        }
+        if (CloseCaptureSessionOnVideoQuirk.isEnabled()) {
+            quirks.add(CloseCaptureSessionOnVideoQuirk())
+        }
         if (ConfigureSurfaceToSecondarySessionFailQuirk.isEnabled(cameraMetadata)) {
             quirks.add(ConfigureSurfaceToSecondarySessionFailQuirk())
         }
+        if (FinalizeSessionOnCloseQuirk.isEnabled()) {
+            quirks.add(FinalizeSessionOnCloseQuirk())
+        }
         if (FlashTooSlowQuirk.isEnabled(cameraMetadata)) {
             quirks.add(FlashTooSlowQuirk())
         }
@@ -92,13 +101,13 @@
         if (YuvImageOnePixelShiftQuirk.isEnabled()) {
             quirks.add(YuvImageOnePixelShiftQuirk())
         }
-        if (CaptureSessionStuckQuirk.isEnabled()) {
-            quirks.add(CaptureSessionStuckQuirk())
-        }
-        if (FinalizeSessionOnCloseQuirk.isEnabled()) {
-            quirks.add(FinalizeSessionOnCloseQuirk())
-        }
 
         Quirks(quirks)
     }
+
+    companion object {
+        fun isImmediateSurfaceReleaseAllowed(): Boolean {
+            return Build.BRAND == "google"
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
new file mode 100644
index 0000000..caad5f5
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.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.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+
+/**
+ * A quirk that instructs the CameraGraph to close the capture session when it is stopped or closed.
+ *
+ * QuirkSummary
+ * - Bug Id:      277310425
+ * - Description: When we unbind and rebind a VideoCapture use case, the video setup process waits
+ *                for the Surfaces to be released. This creates a potential deadlock, as CameraPipe
+ *                may not release the Surfaces if the previous session or camera remains unclosed.
+ *                This is a quirk added so that when we create a CameraGraph out of a set of use
+ *                cases with VideoCapture, we make sure we explicitly instruct CameraGraph to close
+ *                the capture session when the graph is closed.
+ * - Device(s):   All devices.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class CloseCaptureSessionOnVideoQuirk : Quirk {
+    companion object {
+        fun isEnabled(): Boolean = true
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
index 95a6727..ed333d1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
@@ -44,11 +44,19 @@
         fun isEnabled() = true
 
         fun getBehavior() =
-            if (Build.BRAND == "google") {
+            if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
                 // Finalize immediately for devices that allow immediate Surface reuse.
                 FinalizeSessionOnCloseBehavior.IMMEDIATE
-            } else {
+            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+                // When CloseCaptureSessionOnVideoQuirk is enabled, we close the capture session
+                // in anticipation that the onClosed() callback would finalize the session. However,
+                // on API levels < M, it could be possible that onClosed() isn't invoked if a new
+                // capture session (or CameraGraph) is created too soon (read b/144817309 or
+                // CaptureSessionOnClosedNotCalledQuirk for more context). Therefore, we're enabling
+                // this quirk (on a timeout) for API levels < M, too.
                 FinalizeSessionOnCloseBehavior.TIMEOUT
+            } else {
+                FinalizeSessionOnCloseBehavior.OFF
             }
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
index 0a3c631..dac86b9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
@@ -28,12 +28,13 @@
  * Quirk denoting the video profile list returns by [EncoderProfiles] is invalid.
  *
  * QuirkSummary
- * - Bug Id: 267727595
+ * - Bug Id: 267727595, 278860860
  * - Description: When using [EncoderProfiles] on TP1A or TD1A builds of Android API 33,
  *   [EncoderProfiles.getVideoProfiles] returns a list with size one, but the single value in the
  *   list is null. This is not the expected behavior, and makes [EncoderProfiles] lack of video
  *   information.
- * - Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33).
+ * - Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
+ *              with TP1A build (API 33).
  *
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
@@ -41,7 +42,7 @@
 class InvalidVideoProfilesQuirk : Quirk {
 
     companion object {
-        private val AFFECTED_MODELS: List<String> = listOf(
+        private val AFFECTED_PIXEL_MODELS: List<String> = listOf(
             "pixel 4",
             "pixel 4a",
             "pixel 4a (5g)",
@@ -56,25 +57,33 @@
         )
 
         fun isEnabled(): Boolean {
-            return isAffectedModel() && isAffectedBuild()
+            return isAffectedSamsungDevices() || isAffectedPixelDevices()
         }
 
-        private fun isAffectedModel(): Boolean {
-            return AFFECTED_MODELS.contains(
+        private fun isAffectedSamsungDevices(): Boolean {
+            return "samsung".equals(Build.BRAND, true) && isTp1aBuild()
+        }
+
+        private fun isAffectedPixelDevices(): Boolean {
+            return isAffectedPixelModel() && isAffectedPixelBuild()
+        }
+
+        private fun isAffectedPixelModel(): Boolean {
+            return AFFECTED_PIXEL_MODELS.contains(
                 Build.MODEL.lowercase()
             )
         }
 
-        private fun isAffectedBuild(): Boolean {
+        private fun isAffectedPixelBuild(): Boolean {
             return isTp1aBuild() || isTd1aBuild()
         }
 
         private fun isTp1aBuild(): Boolean {
-            return Build.ID.startsWith("TP1A")
+            return Build.ID.startsWith("TP1A", true)
         }
 
         private fun isTd1aBuild(): Boolean {
-            return Build.ID.startsWith("TD1A")
+            return Build.ID.startsWith("TD1A", true)
         }
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index d8e75a8..e05349a1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -18,6 +18,7 @@
 
 package androidx.camera.camera2.pipe.integration.config
 
+import android.media.MediaCodec
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
@@ -30,6 +31,8 @@
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter.Companion.toCamera2ImplConfig
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnVideoQuirk
 import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
 import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
@@ -82,6 +85,7 @@
 class UseCaseCameraConfig(
     private val useCases: List<UseCase>,
     private val cameraStateAdapter: CameraStateAdapter,
+    private val cameraQuirks: CameraQuirks,
     private val cameraGraphFlags: CameraGraph.Flags,
 ) {
     @UseCaseCameraScope
@@ -106,6 +110,7 @@
     ): UseCaseGraphConfig {
         val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
 
+        var containsVideo = false
         // TODO: This may need to combine outputs that are (or will) share the same output
         //  imageReader or surface.
         val sessionConfigAdapter = SessionConfigAdapter(useCases)
@@ -134,9 +139,26 @@
                     "Prepare config for: $deferrableSurface (${deferrableSurface.prescribedSize}," +
                         " ${deferrableSurface.prescribedStreamFormat})"
                 }
+                if (deferrableSurface.containerClass == MediaCodec::class.java) {
+                    containsVideo = true
+                }
             }
         }
 
+        val shouldCloseCaptureSessionOnDisconnect =
+            if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
+                // If we can release Surfaces immediately, we'll finalize the session when the
+                // camera graph is closed (through FinalizeSessionOnCloseQuirk), and thus we won't
+                // need to explicitly close the capture session.
+                false
+            } else {
+                cameraQuirks.quirks.contains(CloseCaptureSessionOnVideoQuirk::class.java) &&
+                    containsVideo
+            }
+        val combinedFlags = cameraGraphFlags.copy(
+            quirkCloseCaptureSessionOnDisconnect = shouldCloseCaptureSessionOnDisconnect,
+        )
+
         // Build up a config (using TEMPLATE_PREVIEW by default)
         // TODO(b/277310425): Turn off CameraGraph.Flags.quirkFinalizeSessionOnCloseBehavior when
         //  it's not needed. This should be needed only when all use cases are detached (with
@@ -146,7 +168,7 @@
                 camera = cameraConfig.cameraId,
                 streams = streamConfigMap.keys.toList(),
                 defaultListeners = listOf(callbackMap, requestListener),
-                flags = cameraGraphFlags,
+                flags = combinedFlags,
             )
         )
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index a19f296..6929c99 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -32,7 +32,6 @@
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.StreamId
-import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.adapter.CameraUseCaseAdapter
 import androidx.camera.camera2.pipe.integration.adapter.CaptureResultAdapter
 import androidx.camera.camera2.pipe.integration.config.CameraScope
@@ -88,8 +87,6 @@
                         )
                     }
                 }
-            } else {
-                Log.error { "Unhandled callback for onBufferLost()" }
             }
         }
     }
@@ -209,8 +206,6 @@
                         )
                     }
                 }
-            } else {
-                Log.error { "Unhandled callback for onRequestSequenceCompleted()" }
             }
         }
     }
@@ -232,8 +227,6 @@
                         )
                     }
                 }
-            } else {
-                Log.error { "Unhandled callback for onStarted()" }
             }
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index bfa1fc7..beb29c8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -34,6 +34,7 @@
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.TorchState
+import androidx.camera.camera2.pipe.core.Log.debug
 import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
@@ -179,6 +180,7 @@
         tags: Map<String, Any>,
         listeners: Set<Request.Listener>
     ): Deferred<Unit> = synchronized(lock) {
+        debug { "[$type] Add request option: $values" }
         infoBundleMap.getOrPut(type) { InfoBundle() }.let {
             it.options.addAllCaptureRequestOptionsWithPriority(values, optionPriority)
             it.tags.putAll(tags)
@@ -195,6 +197,7 @@
         template: RequestTemplate?,
         listeners: Set<Request.Listener>
     ): Deferred<Unit> = synchronized(lock) {
+        debug { "[$type] Set config: ${config?.toParameters()}" }
         infoBundleMap[type] = InfoBundle(
             Camera2ImplConfig.Builder().apply {
                 config?.let {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index 57cfef6..ddf68c2 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -29,6 +29,7 @@
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
 import javax.inject.Inject
@@ -254,7 +255,7 @@
                     result?.let { result ->
                         updateSignals.add(RequestSignal(submittedRequestCounter.value, result))
                     }
-
+                    Log.debug { "Update RepeatingRequest: $request" }
                     it.startRepeating(request)
                 }
             }
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 222047d..9f39981 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
@@ -23,6 +23,7 @@
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.CameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
@@ -72,6 +73,7 @@
     private val controls: java.util.Set<UseCaseCameraControl>,
     private val camera2CameraControl: Camera2CameraControl,
     private val cameraStateAdapter: CameraStateAdapter,
+    private val cameraQuirks: CameraQuirks,
     private val cameraGraphFlags: CameraGraph.Flags,
     cameraProperties: CameraProperties,
     displayInfoManager: DisplayInfoManager,
@@ -257,7 +259,14 @@
 
         // Create and configure the new camera component.
         _activeComponent =
-            builder.config(UseCaseCameraConfig(useCases, cameraStateAdapter, cameraGraphFlags))
+            builder.config(
+                UseCaseCameraConfig(
+                    useCases,
+                    cameraStateAdapter,
+                    cameraQuirks,
+                    cameraGraphFlags
+                )
+            )
                 .build()
         for (control in allControls) {
             control.useCaseCamera = camera
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 f0a9f17..58e25df 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
@@ -22,6 +22,9 @@
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera.RunningUseCasesChangeListener
 import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
@@ -29,6 +32,7 @@
 import androidx.camera.camera2.pipe.integration.testing.FakeCamera2CameraControlCompat
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
 import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCameraComponentBuilder
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCase
@@ -276,6 +280,10 @@
         ),
         cameraStateAdapter = CameraStateAdapter(),
         cameraGraphFlags = CameraGraph.Flags(),
+        cameraQuirks = CameraQuirks(
+            FakeCameraMetadata(),
+            StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
+        ),
         displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext()),
     ).also {
         useCaseManagerList.add(it)
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
index 5c3279f..ca33199f 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
@@ -26,10 +26,14 @@
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.Config
@@ -40,8 +44,12 @@
 import kotlinx.coroutines.withTimeoutOrNull
 
 class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
+    private val cameraQuirks = CameraQuirks(
+        FakeCameraMetadata(),
+        StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
+    )
     private var config: UseCaseCameraConfig =
-        UseCaseCameraConfig(emptyList(), CameraStateAdapter(), CameraGraph.Flags())
+        UseCaseCameraConfig(emptyList(), CameraStateAdapter(), cameraQuirks, CameraGraph.Flags())
 
     override fun config(config: UseCaseCameraConfig): UseCaseCameraComponent.Builder {
         this.config = config
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index 7e5ce4b..ee57224 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -153,7 +153,30 @@
          */
         val quirkWaitForRepeatingRequestOnDisconnect: Boolean? = null,
 
+        /**
+         * A quirk that finalizes [androidx.camera.camera2.pipe.compat.CaptureSessionState] when
+         * the CameraGraph is stopped or closed. When a CameraGraph is started, the app might
+         * wait for the Surfaces to be released before setting the new Surfaces. This creates a
+         * potential deadlock, and this quirk is aimed to mitigate such behavior by releasing the
+         * Surfaces (finalizing the session) when the graph is stopped or closed.
+         *
+         * - Bug(s): b/277310425
+         * - Device(s): All (but behaviors might differ across devices)
+         * - API levels: All
+         */
         val quirkFinalizeSessionOnCloseBehavior: FinalizeSessionOnCloseBehavior = OFF,
+
+        /**
+         * A quirk that closes the camera capture session when the CameraGraph is stopped or closed.
+         * This is needed in cases where the app that do not wish to receive further frames, or
+         * in cases where not closing the capture session before closing the camera device might
+         * cause the camera close call itself to hang indefinitely.
+         *
+         * - Bug(s): b/277310425, b/277310425
+         * - Device(s): Depends on the situation and the use case.
+         * - API levels: All
+         */
+        val quirkCloseCaptureSessionOnDisconnect: Boolean = false,
     ) {
 
         @JvmInline
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
index 35995c4..3d4ea8d 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
@@ -101,7 +101,7 @@
             captureSequenceProcessorFactory,
             cameraSurfaceManager,
             timeSource,
-            config.flags.quirkFinalizeSessionOnCloseBehavior,
+            config.flags,
             scope
         )
         currentSession = session
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
index 0852ebf..7a07920 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
@@ -22,6 +22,7 @@
 import android.view.Surface
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.StreamId
@@ -65,7 +66,7 @@
     private val captureSequenceProcessorFactory: Camera2CaptureSequenceProcessorFactory,
     private val cameraSurfaceManager: CameraSurfaceManager,
     private val timeSource: TimeSource,
-    private val finalizeSessionOnCloseBehavior: FinalizeSessionOnCloseBehavior,
+    private val cameraGraphFlags: CameraGraph.Flags,
     private val scope: CoroutineScope
 ) : CameraCaptureSessionWrapper.StateCallback {
     private val debugId = captureSessionDebugIds.incrementAndGet()
@@ -270,7 +271,7 @@
         val graphProcessor = configuredCaptureSession?.processor
         if (graphProcessor != null) {
             // WARNING:
-            // This does NOT call close on the captureSession to avoid potentially slow
+            // This normally does NOT call close on the captureSession to avoid potentially slow
             // reconfiguration during mode switch and shutdown. This avoids unintentional restarts
             // by clearing the internal captureSession variable, clearing all repeating requests,
             // and by aborting any pending single requests.
@@ -294,6 +295,23 @@
                 Debug.traceStop()
             }
 
+            // There are rare, extraordinary circumstances where we might need to close the capture
+            // session. It is possible the app might explicitly wait for the captures to be
+            // completely stopped through signals from CameraSurfaceManager, and in which case
+            // closing the capture session would eventually release the Surfaces [1]. Additionally,
+            // on certain devices, we need to close the capture session, or else the camera device
+            // close call might stall indefinitely [2].
+            //
+            // [1] b/277310425
+            // [2] b/277675483
+            if (cameraGraphFlags.quirkCloseCaptureSessionOnDisconnect) {
+                val captureSession = configuredCaptureSession?.session
+                checkNotNull(captureSession)
+                Debug.trace("$this CameraCaptureSessionWrapper#close") {
+                    Log.debug { "Closing capture session for $this" }
+                    captureSession.close()
+                }
+            }
             Debug.traceStop()
         }
 
@@ -306,15 +324,17 @@
             if (state != State.CLOSED) {
                 if (_cameraDevice == null || !hasAttemptedCaptureSession) {
                     shouldFinalizeSession = true
-                } else if (finalizeSessionOnCloseBehavior ==
-                    FinalizeSessionOnCloseBehavior.IMMEDIATE
-                ) {
-                    shouldFinalizeSession = true
-                } else if (finalizeSessionOnCloseBehavior ==
-                    FinalizeSessionOnCloseBehavior.TIMEOUT
-                ) {
-                    shouldFinalizeSession = true
-                    finalizeSessionDelayMs = 2000L
+                } else {
+                    when (cameraGraphFlags.quirkFinalizeSessionOnCloseBehavior) {
+                        FinalizeSessionOnCloseBehavior.IMMEDIATE -> {
+                            shouldFinalizeSession = true
+                        }
+
+                        FinalizeSessionOnCloseBehavior.TIMEOUT -> {
+                            shouldFinalizeSession = true
+                            finalizeSessionDelayMs = 2000L
+                        }
+                    }
                 }
             }
             _cameraDevice = null
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
index 3635a7c..1236721 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
@@ -23,6 +23,7 @@
 import android.util.Size
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraPipe
@@ -128,7 +129,10 @@
                     },
                     CameraSurfaceManager(),
                     SystemTimeSource(),
-                    CameraGraph.Flags.FinalizeSessionOnCloseBehavior.OFF,
+                    CameraGraph.Flags(
+                        quirkFinalizeSessionOnCloseBehavior = FinalizeSessionOnCloseBehavior.OFF,
+                        quirkCloseCaptureSessionOnDisconnect = false,
+                    ),
                     this
                 )
             )
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
index 45f4731..eafc0ae 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
@@ -19,6 +19,7 @@
 import android.graphics.SurfaceTexture
 import android.os.Build
 import android.view.Surface
+import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.CaptureSequenceProcessor
@@ -61,6 +62,10 @@
             ): CaptureSequenceProcessor<Request, FakeCaptureSequence> = fakeCaptureSequenceProcessor
         }
     private val timeSource = SystemTimeSource()
+    private val cameraGraphFlags = CameraGraph.Flags(
+        quirkFinalizeSessionOnCloseBehavior = FinalizeSessionOnCloseBehavior.OFF,
+        quirkCloseCaptureSessionOnDisconnect = false,
+    )
 
     private val surface1: Surface = Surface(SurfaceTexture(1))
     private val surface2: Surface = Surface(SurfaceTexture(2))
@@ -91,7 +96,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
         // When disconnect is called first
@@ -114,7 +119,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
 
@@ -142,7 +147,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
 
@@ -176,7 +181,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
         // When surfaces are configured
@@ -200,7 +205,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
         // When surfaces are configured
@@ -224,7 +229,7 @@
                 captureSequenceProcessorFactory,
                 cameraSurfaceManager,
                 timeSource,
-                FinalizeSessionOnCloseBehavior.OFF,
+                cameraGraphFlags,
                 this
             )
         // When surfaces are configured
@@ -238,4 +243,41 @@
         verify(fakeSurfaceListener, times(1)).onSurfaceInactive(eq(surface1))
         verify(fakeSurfaceListener, times(1)).onSurfaceInactive(eq(surface2))
     }
+
+    @Test
+    fun captureSessionStateClosesCaptureSessionWhenQuirkIsEnabled() = runTest {
+        val state =
+            CaptureSessionState(
+                fakeGraphListener,
+                captureSessionFactory,
+                captureSequenceProcessorFactory,
+                cameraSurfaceManager,
+                timeSource,
+                CameraGraph.Flags(
+                    quirkCloseCaptureSessionOnDisconnect = true,
+                ),
+                this
+            )
+
+        // When surfaces are configured
+        state.configureSurfaceMap(mapOf(stream1 to surface1, stream2 to surface2))
+        verify(fakeSurfaceListener, times(1)).onSurfaceActive(eq(surface1))
+        verify(fakeSurfaceListener, times(1)).onSurfaceActive(eq(surface2))
+
+        // And a device is set
+        state.cameraDevice = fakeCameraDevice
+
+        // Advance to make sure a capture session is created.
+        advanceUntilIdle()
+
+        // Feed a fake capture session
+        state.onConfigured(fakeCaptureSession)
+
+        // And the state is then disconnected
+        state.disconnect()
+
+        // Then make sure we do close the capture session.
+        advanceUntilIdle()
+        verify(fakeCaptureSession, times(1)).close()
+    }
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
index c54cc1a..2f3e542 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
@@ -57,6 +57,8 @@
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.camera2.internal.compat.quirk.CameraQuirks;
+import androidx.camera.camera2.internal.compat.workaround.AutoFlashAEModeDisabler;
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraXConfig;
@@ -68,6 +70,7 @@
 import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.CameraControlInternal;
 import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
@@ -125,6 +128,7 @@
     private boolean mHasFlashUnit;
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private CameraUseCaseAdapter mCamera;
+    private Quirks mCameraQuirks;
 
     @Before
     public void setUp() throws InterruptedException {
@@ -151,6 +155,7 @@
                 mCameraCharacteristics, cameraId);
         mCamera2CameraControlImpl = new Camera2CameraControlImpl(mCameraCharacteristicsCompat,
                 executorService, executorService, mControlUpdateCallback);
+        mCameraQuirks = CameraQuirks.get(cameraId, mCameraCharacteristicsCompat);
 
         mCamera2CameraControlImpl.incrementUseCount();
         mCamera2CameraControlImpl.setActive(true);
@@ -724,9 +729,12 @@
     }
 
     private void assertAeMode(Camera2ImplConfig config, int aeMode) {
-        if (isAeModeSupported(aeMode)) {
+        AutoFlashAEModeDisabler aeModeCorrector = new AutoFlashAEModeDisabler(mCameraQuirks);
+        int aeModeCorrected = aeModeCorrector.getCorrectedAeMode(aeMode);
+
+        if (isAeModeSupported(aeModeCorrected)) {
             assertThat(config.getCaptureRequestOption(
-                    CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(aeMode);
+                    CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(aeModeCorrected);
         } else {
             int fallbackMode;
             if (isAeModeSupported(CONTROL_AE_MODE_ON)) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
index 754b174..693f94e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
@@ -30,6 +30,7 @@
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.impl.Camera2CameraCaptureResultConverter
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat
 import androidx.camera.camera2.internal.compat.quirk.CameraQuirks
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks
 import androidx.camera.camera2.internal.util.RequestProcessorRequest
@@ -79,6 +80,7 @@
 
     private lateinit var cameraDeviceHolder: CameraUtil.CameraDeviceHolder
     private lateinit var captureSessionRepository: CaptureSessionRepository
+    private lateinit var dynamicRangesCompat: DynamicRangesCompat
     private lateinit var captureSessionOpenerBuilder: SynchronizedCaptureSessionOpener.Builder
     private lateinit var mainThreadExecutor: Executor
     private lateinit var previewSurface: SessionProcessorSurface
@@ -103,12 +105,14 @@
         val handler = Handler(Looper.getMainLooper())
         mainThreadExecutor = CameraXExecutors.newHandlerExecutor(handler)
         captureSessionRepository = CaptureSessionRepository(mainThreadExecutor)
+        val cameraCharacteristics = getCameraCharacteristic(CAMERA_ID)
+        dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics(cameraCharacteristics)
         captureSessionOpenerBuilder = SynchronizedCaptureSessionOpener.Builder(
             mainThreadExecutor,
             mainThreadExecutor as ScheduledExecutorService,
             handler,
             captureSessionRepository,
-            CameraQuirks.get(CAMERA_ID, getCameraCharacteristic(CAMERA_ID)),
+            CameraQuirks.get(CAMERA_ID, cameraCharacteristics),
             DeviceQuirks.getAll()
         )
 
@@ -171,7 +175,7 @@
     @Test
     fun canSubmit(): Unit = runBlocking {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
@@ -220,7 +224,7 @@
     @Test
     fun canSubmitMultipleThreads() {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
@@ -286,7 +290,7 @@
     @Test
     fun canSubmitList(): Unit = runBlocking {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
@@ -364,7 +368,7 @@
     @Test
     fun canSetRepeating(): Unit = runBlocking {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
@@ -416,7 +420,7 @@
     @Test
     fun closeRequestProcessor(): Unit = runBlocking {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
@@ -459,7 +463,7 @@
     @Test
     fun invalidRequest(): Unit = runBlocking {
         // Arrange
-        val captureSession = CaptureSession()
+        val captureSession = CaptureSession(dynamicRangesCompat)
         val cameraDevice = cameraDeviceHolder.get()!!
         captureSession.open(
             getSessionConfig(),
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index 75e0fe5..1f1834bf 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -16,9 +16,18 @@
 
 package androidx.camera.camera2.internal;
 
+import static android.hardware.DataSpace.STANDARD_BT2020;
+import static android.hardware.DataSpace.TRANSFER_GAMMA2_2;
+import static android.hardware.DataSpace.TRANSFER_GAMMA2_6;
+import static android.hardware.DataSpace.TRANSFER_GAMMA2_8;
+import static android.hardware.DataSpace.TRANSFER_HLG;
+import static android.hardware.DataSpace.TRANSFER_SMPTE_170M;
+import static android.hardware.DataSpace.TRANSFER_SRGB;
+import static android.hardware.DataSpace.TRANSFER_UNSPECIFIED;
 import static android.os.Build.VERSION.SDK_INT;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static junit.framework.TestCase.assertTrue;
 import static junit.framework.TestCase.fail;
@@ -39,11 +48,11 @@
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
+import android.hardware.DataSpace;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
@@ -58,17 +67,23 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.impl.CameraEventCallback;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.camera2.internal.CaptureSession.State;
+import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
 import androidx.camera.camera2.internal.compat.quirk.ConfigureSurfaceToSecondarySessionFailQuirk;
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.camera2.internal.compat.quirk.PreviewOrientationIncorrectQuirk;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraCaptureCallbacks;
 import androidx.camera.core.impl.CameraCaptureResult;
@@ -82,8 +97,10 @@
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.os.HandlerCompat;
+import androidx.core.util.Preconditions;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -109,8 +126,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -119,6 +138,7 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Tests for {@link CaptureSession}. This requires an environment where a valid {@link
@@ -131,6 +151,23 @@
 @SdkSuppress(minSdkVersion = 21)
 @RequiresApi(21)
 public final class CaptureSessionTest {
+    private static final DynamicRange DYNAMIC_RANGE_HLG10 =
+            new DynamicRange(DynamicRange.FORMAT_HLG, DynamicRange.BIT_DEPTH_10_BIT);
+
+    // Enumerate possible SDR transfer functions. This may need to be updated if more transfer
+    // functions are added to the DataSpace class.
+    // This set is notably missing the HLG and PQ transfer functions, though HLG could
+    // technically be used with 8-bit for SDR.
+    // We also exclude LINEAR as most devices should at least apply gamma for SDR.
+    private static final HashSet<Integer> POSSIBLE_COLOR_STANDARDS_SDR =
+            new HashSet<>(Arrays.asList(
+                    TRANSFER_UNSPECIFIED, // Some devices may use this as a default for SDR
+                    TRANSFER_GAMMA2_2,
+                    TRANSFER_GAMMA2_6,
+                    TRANSFER_GAMMA2_8,
+                    TRANSFER_SMPTE_170M,
+                    TRANSFER_SRGB));
+
     /** Thread for all asynchronous calls. */
     private static HandlerThread sHandlerThread;
     /** Handler for all asynchronous calls. */
@@ -151,6 +188,10 @@
     private final List<CaptureSession> mCaptureSessions = new ArrayList<>();
     private final List<DeferrableSurface> mDeferrableSurfaces = new ArrayList<>();
 
+    private CameraCharacteristicsCompat mCameraCharacteristics;
+
+    private DynamicRangesCompat mDynamicRangesCompat;
+
     @Rule
     public TestRule getUseCameraRule() {
         if (SDK_INT >= 19) {
@@ -204,7 +245,20 @@
                 mScheduledExecutor, mHandler, mCaptureSessionRepository,
                 new Quirks(new ArrayList<>()), DeviceQuirks.getAll());
 
-        mCameraDeviceHolder = CameraUtil.getCameraDevice(
+        String cameraId = CameraUtil.getBackwardCompatibleCameraIdListOrThrow().get(0);
+        Context context = ApplicationProvider.getApplicationContext();
+        CameraManagerCompat cameraManager = CameraManagerCompat.from(context, mHandler);
+        try {
+            mCameraCharacteristics =
+                    cameraManager.getCameraCharacteristicsCompat(cameraId);
+        } catch (CameraAccessExceptionCompat e) {
+            throw new AssumptionViolatedException("Could not retrieve camera characteristics", e);
+        }
+
+        mDynamicRangesCompat =
+                DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristics);
+
+        mCameraDeviceHolder = CameraUtil.getCameraDevice(cameraId,
                 mCaptureSessionRepository.getCameraStateCallback());
     }
 
@@ -279,17 +333,9 @@
     }
 
     private boolean isLegacyCamera() {
-        String cameraId = CameraUtil.getBackwardCompatibleCameraIdListOrThrow().get(0);
-        Context context = ApplicationProvider.getApplicationContext();
-        CameraManager cameraManager =
-                (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        try {
-            return cameraManager.getCameraCharacteristics(cameraId)
-                    .get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
-                    == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
-        } catch (CameraAccessException e) {
-        }
-        return false;
+        return Preconditions.checkNotNull(mCameraCharacteristics
+                .get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL))
+                == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
     }
 
     // Set stream use case is not supported before API 33
@@ -320,6 +366,27 @@
                 == CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW);
     }
 
+    @SdkSuppress(minSdkVersion = 33) // Can only verify data space on API 33+
+    @Test
+    public void openCaptureSessionWithDefault_usesSdrDynamicRange()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        openCaptureSessionAndVerifyDynamicRangeApplied(
+                /*inputDynamicRange=*/null, // Should default to SDR
+                /*possibleColorStandards=*/null, // Do not check ColorSpace for SDR; could be many.
+                POSSIBLE_COLOR_STANDARDS_SDR
+        );
+    }
+
+    @SdkSuppress(minSdkVersion = 33) // HLG dynamic range only supported since API 33
+    @Test
+    public void openCaptureSessionWithHlgDynamicRange()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        openCaptureSessionAndVerifyDynamicRangeApplied(
+                DYNAMIC_RANGE_HLG10,
+                Collections.singleton(STANDARD_BT2020),
+                Collections.singleton(TRANSFER_HLG));
+    }
+
     // Sharing surface of YUV format is supported since API 28
     @SdkSuppress(minSdkVersion = 28)
     @Test
@@ -1103,7 +1170,7 @@
     }
 
     private CaptureSession createCaptureSession() {
-        CaptureSession captureSession = new CaptureSession();
+        CaptureSession captureSession = new CaptureSession(mDynamicRangesCompat);
         mCaptureSessions.add(captureSession);
         return captureSession;
     }
@@ -1365,7 +1432,7 @@
         SynchronizedCaptureSessionOpener opener = new SynchronizedCaptureSessionOpener(fakeOpener);
         // Don't use #createCaptureSession since FakeOpenerImpl won't create CameraCaptureSession
         // so no need to be released.
-        CaptureSession captureSession = new CaptureSession();
+        CaptureSession captureSession = new CaptureSession(mDynamicRangesCompat);
         captureSession.open(sessionConfigBuilder.build(), mCameraDeviceHolder.get(), opener);
 
         ArgumentCaptor<SessionConfigurationCompat> captor =
@@ -1512,8 +1579,97 @@
         verify(stateCallback, timeout(3000L)).onReady(any());
     }
 
+    @RequiresApi(33) // SurfaceTexture.getDataSpace() was added in API 33
+    private void openCaptureSessionAndVerifyDynamicRangeApplied(
+            @Nullable DynamicRange inputDynamicRange,
+            @Nullable Set<Integer> possibleColorStandards,
+            @Nullable Set<Integer> possibleTransferFns)
+            throws ExecutionException, InterruptedException, TimeoutException {
+        // 1. Arrange
+        if (inputDynamicRange != null) {
+            // Only run test on devices that support the
+            assumeTrue(
+                    mDynamicRangesCompat.getSupportedDynamicRanges().contains(inputDynamicRange));
+        }
+
+        CountDownLatch latch0 = new CountDownLatch(1);
+        AtomicInteger dataSpace = new AtomicInteger(DataSpace.DATASPACE_UNKNOWN);
+        ListenableFuture<SurfaceTextureProvider.SurfaceTextureHolder> surfaceTextureHolderFuture =
+                SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(mExecutor, 640, 480,
+                        surfaceTexture -> {
+                            dataSpace.set(surfaceTexture.getDataSpace());
+                            latch0.countDown();
+                        }, /* onClosed= */null);
+
+        DeferrableSurface deferrableSurface = new DeferrableSurface() {
+            @NonNull
+            @Override
+            protected ListenableFuture<Surface> provideSurface() {
+                return Futures.transform(surfaceTextureHolderFuture,
+                        surfaceTextureHolder -> {
+                            Surface surface = new Surface(surfaceTextureHolder.getSurfaceTexture());
+                            getTerminationFuture().addListener(surface::release, mExecutor);
+                            return surface;
+                        },
+                        CameraXExecutors.directExecutor());
+            }
+        };
+
+        deferrableSurface.getTerminationFuture().addListener(
+                () -> Futures.addCallback(surfaceTextureHolderFuture,
+                        new FutureCallback<SurfaceTextureProvider.SurfaceTextureHolder>() {
+                            @Override
+                            public void onSuccess(
+                                    @Nullable SurfaceTextureProvider.SurfaceTextureHolder result) {
+                                try {
+                                    Preconditions.checkNotNull(result).close();
+                                } catch (Exception e) {
+                                    throw new AssertionError("Unable to release SurfaceTexture", e);
+                                }
+                            }
+
+                            @Override
+                            public void onFailure(@NonNull Throwable t) {
+                                throw new AssertionError("Unable to retrieve SurfaceTexture", t);
+                            }
+                        }, mExecutor), CameraXExecutors.directExecutor());
+
+        mDeferrableSurfaces.add(deferrableSurface);
+
+        SessionConfig.OutputConfig.Builder outputConfigBuilder =
+                SessionConfig.OutputConfig.builder(deferrableSurface);
+        if (inputDynamicRange != null) {
+            outputConfigBuilder.setDynamicRange(inputDynamicRange);
+        }
+        SessionConfig sessionConfig =
+                new SessionConfig.Builder()
+                        .addOutputConfig(outputConfigBuilder.build())
+                        .setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
+                        .build();
+
+        // 2. Act
+        CaptureSession captureSession = createCaptureSession();
+        captureSession.setSessionConfig(sessionConfig); // set repeating request
+        ListenableFuture<Void> future = captureSession.open(sessionConfig,
+                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build());
+        future.get(2, TimeUnit.SECONDS);
+
+        // 3. Assert
+        assertWithMessage("Timed out while waiting for frame to be produced.")
+                .that(latch0.await(2, TimeUnit.SECONDS))
+                .isTrue();
+
+        // Ensure the dataspace matches what is expected
+        if (possibleColorStandards != null) {
+            assertThat(DataSpace.getStandard(dataSpace.get())).isIn(possibleColorStandards);
+        }
+        if (possibleTransferFns != null) {
+            assertThat(DataSpace.getTransfer(dataSpace.get())).isIn(possibleTransferFns);
+        }
+    }
+
     /**
-     * A implementation to test {@link CameraEventCallback} on CaptureSession.
+     * A implementation to test {@link CameraEventCallback} on CaptureSession.f
      */
     private static class TestCameraEventCallback extends CameraEventCallback {
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
index 057cc38..0a26bfb 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
@@ -34,6 +34,7 @@
 import android.view.Surface
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks
 import androidx.camera.camera2.interop.CaptureRequestOptions
 import androidx.camera.core.CameraSelector
@@ -195,9 +196,16 @@
 
         val cameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
         val camera2Info = Camera2CameraInfoImpl(cameraId, cameraManagerCompat)
+        val dynamicRangesCompat = cameraManagerCompat.getCameraCharacteristicsCompat(cameraId).let {
+            DynamicRangesCompat.fromCameraCharacteristics(it)
+        }
 
         return ProcessingCaptureSession(
-            sessionProcessor, camera2Info, executor, executor as ScheduledExecutorService
+            sessionProcessor,
+            camera2Info,
+            dynamicRangesCompat,
+            executor,
+            executor as ScheduledExecutorService
         )
     }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 088aa5d..1612a15 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -37,13 +37,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.camera2.internal.annotation.CameraExecutor;
 import androidx.camera.camera2.internal.compat.ApiCompat;
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraState;
@@ -201,6 +201,9 @@
     @NonNull
     private final CameraCharacteristicsCompat mCameraCharacteristicsCompat;
 
+    @NonNull
+    private final DynamicRangesCompat mDynamicRangesCompat;
+
     /**
      * Constructor for a camera.
      *
@@ -234,7 +237,6 @@
         mCameraStateMachine = new CameraStateMachine(cameraStateRegistry);
         mCaptureSessionRepository = new CaptureSessionRepository(mExecutor);
         mDisplayInfoManager = displayInfoManager;
-        mCaptureSession = newCaptureSession();
 
         try {
             mCameraCharacteristicsCompat =
@@ -248,6 +250,10 @@
         } catch (CameraAccessExceptionCompat e) {
             throw CameraUnavailableExceptionHelper.createFrom(e);
         }
+        mDynamicRangesCompat =
+                DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristicsCompat);
+        mCaptureSession = newCaptureSession();
+
         mCaptureSessionOpenerBuilder = new SynchronizedCaptureSessionOpener.Builder(mExecutor,
                 mScheduledExecutorService, schedulerHandler, mCaptureSessionRepository,
                 cameraInfoImpl.getCameraQuirks(), DeviceQuirks.getAll());
@@ -268,10 +274,11 @@
     private CaptureSessionInterface newCaptureSession() {
         synchronized (mLock) {
             if (mSessionProcessor == null) {
-                return new CaptureSession();
+                return new CaptureSession(mDynamicRangesCompat);
             } else {
                 return new ProcessingCaptureSession(mSessionProcessor,
-                        mCameraInfoInternal, mExecutor, mScheduledExecutorService);
+                        mCameraInfoInternal, mDynamicRangesCompat, mExecutor,
+                        mScheduledExecutorService);
             }
         }
     }
@@ -359,7 +366,7 @@
     @ExecutedBy("mExecutor")
     private void configAndClose(boolean abortInFlightCaptures) {
 
-        final CaptureSession noOpSession = new CaptureSession();
+        final CaptureSession noOpSession = new CaptureSession(mDynamicRangesCompat);
 
         mConfiguringForClose.add(noOpSession);  // Make mCameraDevice is not closed and existed.
         resetCaptureSession(abortInFlightCaptures);
@@ -671,7 +678,7 @@
      * block until completion.
      *
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     boolean isUseCaseAttached(@NonNull UseCase useCase) {
         try {
             String useCaseId = getUseCaseId(useCase);
@@ -1023,7 +1030,7 @@
     }
 
     @NonNull
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     public CameraAvailability getCameraAvailability() {
         return mCameraAvailability;
     }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 4894b28..95dda70 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -22,6 +22,8 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.os.Build;
 import android.view.Surface;
 
 import androidx.annotation.GuardedBy;
@@ -31,12 +33,15 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.camera2.internal.compat.params.DynamicRangeConversions;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
 import androidx.camera.camera2.internal.compat.params.InputConfigurationCompat;
 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
 import androidx.camera.camera2.internal.compat.workaround.StillCaptureFlow;
 import androidx.camera.camera2.internal.compat.workaround.TorchStateReset;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.Logger;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CaptureConfig;
@@ -135,11 +140,14 @@
     final StillCaptureFlow mStillCaptureFlow = new StillCaptureFlow();
     final TorchStateReset mTorchStateReset = new TorchStateReset();
 
+    private final DynamicRangesCompat mDynamicRangesCompat;
+
     /**
      * Constructor for CaptureSession.
      */
-    CaptureSession() {
+    CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat) {
         mState = State.INITIALIZED;
+        mDynamicRangesCompat = dynamicRangesCompat;
         mCaptureSessionStateCallback = new StateCallback();
     }
 
@@ -416,6 +424,27 @@
                 outputConfiguration.addSurface(sharedSurface);
             }
         }
+
+        long dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            DynamicRangeProfiles dynamicRangeProfiles =
+                    mDynamicRangesCompat.toDynamicRangeProfiles();
+            if (dynamicRangeProfiles != null) {
+                DynamicRange requestedDynamicRange = outputConfig.getDynamicRange();
+                Long dynamicRangeProfileOrNull =
+                        DynamicRangeConversions.dynamicRangeToFirstSupportedProfile(
+                                requestedDynamicRange, dynamicRangeProfiles);
+                if (dynamicRangeProfileOrNull == null) {
+                    Logger.e(TAG,
+                            "Requested dynamic range is not supported. Defaulting to STANDARD "
+                                    + "dynamic range profile.\nRequested dynamic range:\n  "
+                                    + requestedDynamicRange);
+                } else {
+                    dynamicRangeProfile = dynamicRangeProfileOrNull;
+                }
+            }
+        }
+        outputConfiguration.setDynamicRangeProfile(dynamicRangeProfile);
         return outputConfiguration;
     }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
index cdab65e..da5e717 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
@@ -26,6 +26,7 @@
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
 import androidx.camera.camera2.interop.CaptureRequestOptions;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.ImageAnalysis;
@@ -123,9 +124,10 @@
     private int mInstanceId = 0;
 
     ProcessingCaptureSession(@NonNull SessionProcessor sessionProcessor,
-            @NonNull Camera2CameraInfoImpl camera2CameraInfoImpl, @NonNull Executor executor,
+            @NonNull Camera2CameraInfoImpl camera2CameraInfoImpl,
+            @NonNull DynamicRangesCompat dynamicRangesCompat, @NonNull Executor executor,
             @NonNull ScheduledExecutorService scheduledExecutorService) {
-        mCaptureSession = new CaptureSession();
+        mCaptureSession = new CaptureSession(dynamicRangesCompat);
         mSessionProcessor = sessionProcessor;
         mCamera2CameraInfoImpl = camera2CameraInfoImpl;
         mExecutor = executor;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
index 0ab747c..1ca1a561 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
@@ -29,7 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
 
 import java.util.Map;
@@ -77,7 +77,7 @@
      * Get a {@link CameraManagerCompat} instance from a provided {@link CameraManagerCompatImpl}.
      *
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public static CameraManagerCompat from(@NonNull final CameraManagerCompatImpl impl) {
         return new CameraManagerCompat(impl);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
index e7d3d64..0af3554 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
@@ -119,7 +119,7 @@
      * Creates an instance from a framework android.hardware.camera2.params.DynamicRangeProfiles
      * object.
      *
-     * @param dynamicRangeProfiles a {@link android.hardware.camera2.params.DynamicRangeProfiles).
+     * @param dynamicRangeProfiles a {@link android.hardware.camera2.params.DynamicRangeProfiles}.
      * @return an equivalent {@link DynamicRangesCompat} object.
      */
     @Nullable
@@ -138,16 +138,17 @@
 
     /**
      * Returns the underlying framework
-     * {@link android.hardware.camera2.params.DynamicRangeProfiles).
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles}.
      *
-     * @return the underlying {@link android.hardware.camera2.params.DynamicRangeProfiles).
+     * @return the underlying {@link android.hardware.camera2.params.DynamicRangeProfiles} or
+     * {@code null} if the device doesn't support 10 bit dynamic range.
      */
-    @NonNull
+    @Nullable
     @RequiresApi(33)
     public DynamicRangeProfiles toDynamicRangeProfiles() {
         Preconditions.checkState(Build.VERSION.SDK_INT >= 33, "DynamicRangesCompat can only be "
                 + "converted to DynamicRangeProfiles on API 33 or higher.");
-        return Preconditions.checkNotNull(mImpl.unwrap());
+        return mImpl.unwrap();
     }
 
     interface DynamicRangeProfilesCompatImpl {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
index 5c0a513..ba916eb 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
@@ -30,17 +30,18 @@
  * Quirk denoting the video profile list returns by {@link EncoderProfiles} is invalid.
  *
  * <p>QuirkSummary
- *     Bug Id: 267727595
+ *     Bug Id: 267727595, 278860860
  *     Description: When using {@link EncoderProfiles} on TP1A or TD1A builds of Android API 33,
  *                  {@link EncoderProfiles#getVideoProfiles()} returns a list with size one, but
  *                  the single value in the list is null. This is not the expected behavior, and
  *                  makes {@link EncoderProfiles} lack of video information.
- *     Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33).
+ *     Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
+ *                with TP1A build (API 33).
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class InvalidVideoProfilesQuirk implements Quirk {
 
-    static final List<String> AFFECTED_MODELS = Arrays.asList(
+    static final List<String> AFFECTED_PIXEL_MODELS = Arrays.asList(
             "pixel 4",
             "pixel 4a",
             "pixel 4a (5g)",
@@ -55,22 +56,30 @@
     );
 
     static boolean load() {
-        return isAffectedModel() && isAffectedBuild();
+        return isAffectedSamsungDevices() || isAffectedPixelDevices();
     }
 
-    private static boolean isAffectedModel() {
-        return AFFECTED_MODELS.contains(Build.MODEL.toLowerCase(Locale.US));
+    private static boolean isAffectedSamsungDevices() {
+        return "samsung".equalsIgnoreCase(Build.BRAND) && isTp1aBuild();
     }
 
-    private static boolean isAffectedBuild() {
+    private static boolean isAffectedPixelDevices() {
+        return isAffectedPixelModel() && isAffectedPixelBuild();
+    }
+
+    private static boolean isAffectedPixelModel() {
+        return AFFECTED_PIXEL_MODELS.contains(Build.MODEL.toLowerCase(Locale.ROOT));
+    }
+
+    private static boolean isAffectedPixelBuild() {
         return isTp1aBuild() || isTd1aBuild();
     }
 
     private static boolean isTp1aBuild() {
-        return Build.ID.startsWith("TP1A");
+        return Build.ID.toLowerCase(Locale.ROOT).startsWith("tp1a");
     }
 
     private static boolean isTd1aBuild() {
-        return Build.ID.startsWith("TD1A");
+        return Build.ID.toLowerCase(Locale.ROOT).startsWith("td1a");
     }
 }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
index 76fdf54..56d280e 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
@@ -177,6 +177,16 @@
 
     @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
     @Test
+    fun producesNullDynamicRangeProfilesFromNullCharacteristics() {
+        val characteristics = newCameraCharacteristicsCompat()
+
+        val dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics(characteristics)
+
+        assertThat(dynamicRangesCompat.toDynamicRangeProfiles()).isNull()
+    }
+
+    @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
+    @Test
     fun canProduceDynamicRangesCompatFromCharacteristics() {
         val characteristics = newCameraCharacteristicsCompat()
         Shadow.extract<ShadowCameraCharacteristics>(
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 411d40e..cfeb65b 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -213,7 +213,7 @@
     method public boolean isOutputImageRotationEnabled();
     method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     field public static final int COORDINATE_SYSTEM_ORIGINAL = 0; // 0x0
     field public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; // 0x2
     field public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; // 0x1
@@ -253,7 +253,7 @@
     method public void setCropAspectRatio(android.util.Rational);
     method public void setFlashMode(int);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
     method public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
     field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
@@ -475,6 +475,7 @@
   }
 
   @RequiresApi(21) public abstract class UseCase {
+    method public static int snapToSurfaceRotation(@IntRange(from=0, to=359) int);
   }
 
   @RequiresApi(21) public final class UseCaseGroup {
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index 7be94b5..b26406f 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -228,7 +228,7 @@
     method public boolean isOutputImageRotationEnabled();
     method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     field public static final int COORDINATE_SYSTEM_ORIGINAL = 0; // 0x0
     field public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; // 0x2
     field public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; // 0x1
@@ -268,7 +268,7 @@
     method public void setCropAspectRatio(android.util.Rational);
     method public void setFlashMode(int);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
     method public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
     field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
@@ -492,6 +492,7 @@
   }
 
   @RequiresApi(21) public abstract class UseCase {
+    method public static int snapToSurfaceRotation(@IntRange(from=0, to=359) int);
   }
 
   @RequiresApi(21) public final class UseCaseGroup {
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 411d40e..cfeb65b 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -213,7 +213,7 @@
     method public boolean isOutputImageRotationEnabled();
     method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     field public static final int COORDINATE_SYSTEM_ORIGINAL = 0; // 0x0
     field public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; // 0x2
     field public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; // 0x1
@@ -253,7 +253,7 @@
     method public void setCropAspectRatio(android.util.Rational);
     method public void setFlashMode(int);
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
     method public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
     field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
@@ -475,6 +475,7 @@
   }
 
   @RequiresApi(21) public abstract class UseCase {
+    method public static int snapToSurfaceRotation(@IntRange(from=0, to=359) int);
   }
 
   @RequiresApi(21) public final class UseCaseGroup {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
index 71f4651..cbb7e2e 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
+import androidx.camera.core.UseCase.snapToSurfaceRotation
 import androidx.camera.core.concurrent.CameraCoordinator
 import androidx.camera.core.impl.Config
 import androidx.camera.core.impl.ImageOutputConfig
@@ -42,6 +43,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -200,7 +202,7 @@
     @Test
     fun returnNullResolutionInfo_beforeAddingToCameraUseCaseAdapter() {
         val fakeUseCase = FakeUseCase()
-        assertThat(fakeUseCase.resolutionInfo).isNull()
+        assertThat(fakeUseCase.resolutionInfoInternal).isNull()
     }
 
     @Test
@@ -209,7 +211,7 @@
         val fakeUseCase = FakeUseCase()
         val cameraUseCaseAdapter = createCameraUseCaseAdapter()
         cameraUseCaseAdapter.addUseCases(listOf<UseCase>(fakeUseCase))
-        val resolutionInfo = fakeUseCase.resolutionInfo
+        val resolutionInfo = fakeUseCase.resolutionInfoInternal
         assertThat(resolutionInfo).isNotNull()
         assertThat(resolutionInfo!!.resolution).isEqualTo(SURFACE_RESOLUTION)
         assertThat(resolutionInfo.cropRect).isEqualTo(
@@ -228,7 +230,7 @@
         val cameraUseCaseAdapter = createCameraUseCaseAdapter()
         cameraUseCaseAdapter.addUseCases(listOf<UseCase>(fakeUseCase))
         cameraUseCaseAdapter.removeUseCases(listOf<UseCase>(fakeUseCase))
-        val resolutionInfo = fakeUseCase.resolutionInfo
+        val resolutionInfo = fakeUseCase.resolutionInfoInternal
         assertThat(resolutionInfo).isNull()
     }
 
@@ -239,7 +241,7 @@
         fakeUseCase.targetRotationInternal = Surface.ROTATION_90
         val cameraUseCaseAdapter = createCameraUseCaseAdapter()
         cameraUseCaseAdapter.addUseCases(listOf<UseCase>(fakeUseCase))
-        val resolutionInfo = fakeUseCase.resolutionInfo
+        val resolutionInfo = fakeUseCase.resolutionInfoInternal
         assertThat(resolutionInfo!!.rotationDegrees).isEqualTo(270)
     }
 
@@ -255,7 +257,7 @@
             )
         )
         cameraUseCaseAdapter.addUseCases(listOf<UseCase>(fakeUseCase))
-        val resolutionInfo = fakeUseCase.resolutionInfo
+        val resolutionInfo = fakeUseCase.resolutionInfoInternal
         assertThat(resolutionInfo!!.cropRect).isEqualTo(Rect(0, 60, 640, 420))
     }
 
@@ -292,6 +294,24 @@
         assertThat(fakeUseCase.isMirroringRequired(fakeFrontCamera)).isTrue()
     }
 
+    @Test
+    fun snapToSurfaceRotation_toCorrectValue() {
+        assertThat(snapToSurfaceRotation(45)).isEqualTo(Surface.ROTATION_270)
+        assertThat(snapToSurfaceRotation(135)).isEqualTo(Surface.ROTATION_180)
+        assertThat(snapToSurfaceRotation(225)).isEqualTo(Surface.ROTATION_90)
+        assertThat(snapToSurfaceRotation(315)).isEqualTo(Surface.ROTATION_0)
+    }
+
+    @Test
+    fun snapToSurfaceRotation_invalidInput() {
+        assertThrows<IllegalArgumentException> {
+            snapToSurfaceRotation(-1)
+        }
+        assertThrows<IllegalArgumentException> {
+            snapToSurfaceRotation(360)
+        }
+    }
+
     private fun createFakeUseCase(
         targetRotation: Int = Surface.ROTATION_0,
         mirrorMode: Int? = null,
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
index 3dfd2da..2515afd 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
@@ -106,7 +106,7 @@
     private suspend fun processYuvAndVerifyOutputSize(outputFileOptions: OutputFileOptions?) {
         // Arrange: create node with JPEG input and grayscale effect.
         val node = ProcessingNode(mainThreadExecutor())
-        val nodeIn = ProcessingNode.In.of(ImageFormat.YUV_420_888)
+        val nodeIn = ProcessingNode.In.of(ImageFormat.YUV_420_888, ImageFormat.JPEG)
         val imageIn = createYuvFakeImageProxy(
             CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
             WIDTH,
@@ -126,7 +126,7 @@
             mainThreadExecutor(),
             InternalImageProcessor(GrayscaleImageEffect())
         )
-        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG)
+        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
         val imageIn = createJpegFakeImageProxy(
             CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
             createJpegBytes(WIDTH, HEIGHT)
@@ -175,7 +175,7 @@
     ) {
         // Arrange: create a request with no cropping
         val node = ProcessingNode(mainThreadExecutor())
-        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG)
+        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
         node.transform(nodeIn)
         val takePictureCallback = FakeTakePictureCallback()
 
@@ -213,7 +213,7 @@
     private suspend fun inMemoryInputPacket_callbackInvoked(outputFileOptions: OutputFileOptions?) {
         // Arrange.
         val node = ProcessingNode(mainThreadExecutor())
-        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG)
+        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
         node.transform(nodeIn)
         val takePictureCallback = FakeTakePictureCallback()
 
@@ -246,7 +246,7 @@
     private suspend fun saveJpegOnDisk_verifyOutput(outputFileOptions: OutputFileOptions?) {
         // Arrange: create a on-disk processing request.
         val node = ProcessingNode(mainThreadExecutor())
-        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG)
+        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
         node.transform(nodeIn)
         val takePictureCallback = FakeTakePictureCallback()
         val jpegBytes = ExifUtil.updateExif(createJpegBytes(640, 480)) {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
index 099b42b..0d7695d8e 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
@@ -37,14 +37,15 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import kotlin.coroutines.ContinuationInterceptor
-import kotlin.coroutines.resume
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.android.asCoroutineDispatcher
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
 import org.junit.After
+import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.BeforeClass
 import org.junit.Rule
@@ -147,18 +148,22 @@
         val inputSurface = Surface(surfaceTexture).apply {
             surfacesToRelease.add(this)
         }
-        // Create Bitmap for drawing
-        val inputImage = createBitmap(WIDTH, HEIGHT)
+        // Listen for OnFrameAvailable updates before drawing.
+        val deferredOnFrameAvailable = CompletableDeferred<Unit>()
+        surfaceTexture.setOnFrameAvailableListener({
+            deferredOnFrameAvailable.complete(Unit)
+        }, Handler(Looper.getMainLooper()))
+
         // Draw bitmap to inputSurface.
+        val inputImage = createBitmap(WIDTH, HEIGHT)
         val canvas = inputSurface.lockHardwareCanvas()
         canvas.drawBitmap(inputImage, 0f, 0f, null)
         inputSurface.unlockCanvasAndPost(canvas)
+
         // Wait for frame available and update texture.
-        suspendCancellableCoroutine { continuation ->
-            surfaceTexture.setOnFrameAvailableListener({
-                continuation.resume(Unit)
-            }, Handler(Looper.getMainLooper()))
-        }
+        withTimeoutOrNull(5_000) {
+            deferredOnFrameAvailable.await()
+        } ?: fail("Timed out waiting for SurfaceTexture frame available.")
         surfaceTexture.updateTexImage()
 
         // Act: take a snapshot.
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 c708353..c3c0d6e 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
@@ -41,6 +41,7 @@
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_NAME;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_USE_CASE_EVENT_CALLBACK;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
+import static androidx.camera.core.impl.utils.TransformUtils.within360;
 import static androidx.camera.core.internal.ThreadConfig.OPTION_BACKGROUND_EXECUTOR;
 
 import android.graphics.ImageFormat;
@@ -441,8 +442,7 @@
      * <p>
      * The rotation can be set when constructing an {@link ImageAnalysis} instance using
      * {@link ImageAnalysis.Builder#setTargetRotation(int)}, or dynamically by calling
-     * {@link ImageAnalysis#setTargetRotation(int)} or
-     * {@link ImageAnalysis#setTargetRotationDegrees(int)}. If not set, the target rotation
+     * {@link ImageAnalysis#setTargetRotation(int)}. If not set, the target rotation
      * defaults to the value of {@link Display#getRotation()} of the default display at the time
      * the use case is created. The use case is fully created once it has been attached to a camera.
      * </p>
@@ -471,10 +471,11 @@
      * set the target rotation.  This way, the rotation output to the Analyzer will indicate
      * which way is down for a given image.  This is important since display orientation may be
      * locked by device default, user setting, or app configuration, and some devices may not
-     * transition to a reverse-portrait display orientation.  In these cases, use
-     * {@link ImageAnalysis#setTargetRotationDegrees(int)} to set target rotation dynamically
-     * according to the {@link android.view.OrientationEventListener}, without re-creating the
-     * use case. See {@link #setTargetRotationDegrees} for more information.
+     * transition to a reverse-portrait display orientation. In these cases, set target rotation
+     * dynamically according to the {@link android.view.OrientationEventListener}, without
+     * re-creating the use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to
+     * convert the orientation of the {@link android.view.OrientationEventListener} to a rotation
+     * value. See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code.
      *
      * <p>When this function is called, value set by
      * {@link ImageAnalysis.Builder#setTargetResolution(Size)} will be updated automatically to
@@ -497,6 +498,7 @@
         }
     }
 
+    // TODO(b/277999375): Remove API setTargetRotationDegrees.
     /**
      * Sets the target rotation in degrees.
      *
@@ -560,9 +562,12 @@
      * @param degrees Desired rotation degree of the output image.
      * @see #setTargetRotation(int)
      * @see #getTargetRotation()
+     * @deprecated Use {@link UseCase#snapToSurfaceRotation(int)} and
+     * {@link #setTargetRotation(int)} to convert and set the rotation.
      */
+    @Deprecated // TODO(b/277999375): Remove API setTargetRotationDegrees.
     public void setTargetRotationDegrees(int degrees) {
-        setTargetRotation(orientationDegreesToSurfaceRotation(degrees));
+        setTargetRotation(snapToSurfaceRotation(within360(degrees)));
     }
 
     /**
@@ -724,9 +729,8 @@
      * CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
      */
     @Nullable
-    @Override
     public ResolutionInfo getResolutionInfo() {
-        return super.getResolutionInfo();
+        return getResolutionInfoInternal();
     }
 
     /**
@@ -1315,9 +1319,8 @@
          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
          *
          * <p>In general, it is best to additionally set the target rotation dynamically on the use
-         * case.  See
-         * {@link androidx.camera.core.ImageAnalysis#setTargetRotationDegrees(int)} for additional
-         * documentation.
+         * case. See {@link androidx.camera.core.ImageAnalysis#setTargetRotation(int)} for
+         * additional documentation.
          *
          * <p>If not set, the target rotation will default to the value of
          * {@link android.view.Display#getRotation()} of the default display at the time the
@@ -1326,7 +1329,6 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          * @see androidx.camera.core.ImageAnalysis#setTargetRotation(int)
-         * @see androidx.camera.core.ImageAnalysis#setTargetRotationDegrees(int)
          * @see android.view.OrientationEventListener
          */
         @NonNull
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 06300c0e..4eb88f5 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
@@ -46,6 +46,7 @@
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
 import static androidx.camera.core.impl.utils.TransformUtils.is90or270;
+import static androidx.camera.core.impl.utils.TransformUtils.within360;
 import static androidx.camera.core.internal.utils.ImageUtil.computeCropRectFromAspectRatio;
 import static androidx.camera.core.internal.utils.ImageUtil.isAspectRatioValid;
 import static androidx.core.util.Preconditions.checkNotNull;
@@ -700,8 +701,7 @@
      *
      * <p>The rotation can be set prior to constructing an ImageCapture using
      * {@link ImageCapture.Builder#setTargetRotation(int)} or dynamically by calling
-     * {@link ImageCapture#setTargetRotation(int)} or
-     * {@link ImageCapture#setTargetRotationDegrees(int)}. The rotation of an image taken is
+     * {@link ImageCapture#setTargetRotation(int)}. The rotation of an image taken is
      * determined by the rotation value set at the time image capture is initiated, such as when
      * calling {@link #takePicture(Executor, OnImageCapturedCallback)}.
      *
@@ -731,10 +731,11 @@
      * set the target rotation.  This way, the rotation output will indicate which way is down for
      * a given image.  This is important since display orientation may be locked by device
      * default, user setting, or app configuration, and some devices may not transition to a
-     * reverse-portrait display orientation. In these cases,
-     * use {@link #setTargetRotationDegrees} to set target rotation dynamically according to the
-     * {@link android.view.OrientationEventListener}, without re-creating the use case.
-     * See {@link #setTargetRotationDegrees} for more information.
+     * reverse-portrait display orientation. In these cases, set target rotation dynamically
+     * according to the {@link android.view.OrientationEventListener}, without re-creating the
+     * use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to convert the
+     * orientation of the {@link android.view.OrientationEventListener} to a rotation value.
+     * See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code.
      *
      * <p>When this function is called, value set by
      * {@link ImageCapture.Builder#setTargetResolution(Size)} will be updated automatically to make
@@ -843,9 +844,12 @@
      * @param degrees Desired rotation degree of the output image.
      * @see #setTargetRotation(int)
      * @see #getTargetRotation()
+     * @deprecated Use {@link UseCase#snapToSurfaceRotation(int)} and
+     * {@link #setTargetRotation(int)} to convert and set the rotation.
      */
+    @Deprecated // TODO(b/277999375): Remove API setTargetRotationDegrees.
     public void setTargetRotationDegrees(int degrees) {
-        setTargetRotation(orientationDegreesToSurfaceRotation(degrees));
+        setTargetRotation(snapToSurfaceRotation(within360(degrees)));
     }
 
     /**
@@ -893,9 +897,8 @@
      *, CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
      */
     @Nullable
-    @Override
     public ResolutionInfo getResolutionInfo() {
-        return super.getResolutionInfo();
+        return getResolutionInfoInternal();
     }
 
     /**
@@ -1696,16 +1699,6 @@
     @MainThread
     private boolean isNodeEnabled() {
         checkMainThread();
-        ImageCaptureConfig config = (ImageCaptureConfig) getCurrentConfig();
-        if (config.getImageReaderProxyProvider() != null) {
-            // Use old pipeline for custom ImageReader.
-            return false;
-        }
-
-        if (config.getBufferFormat(ImageFormat.JPEG) != ImageFormat.JPEG) {
-            // Use old pipeline for non-JPEG output format.
-            return false;
-        }
         return mUseProcessingPipeline;
     }
 
@@ -2795,7 +2788,7 @@
          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
          *
          * <p>In general, it is best to additionally set the target rotation dynamically on the use
-         * case.  See {@link androidx.camera.core.ImageCapture#setTargetRotationDegrees(int)} for
+         * case. See {@link androidx.camera.core.ImageCapture#setTargetRotation(int)} for
          * additional documentation.
          *
          * <p>If not set, the target rotation will default to the value of
@@ -2805,7 +2798,6 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          * @see androidx.camera.core.ImageCapture#setTargetRotation(int)
-         * @see androidx.camera.core.ImageCapture#setTargetRotationDegrees(int)
          * @see android.view.OrientationEventListener
          */
         @NonNull
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 5c0b125..30d1109 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
@@ -551,9 +551,8 @@
      * CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
      */
     @Nullable
-    @Override
     public ResolutionInfo getResolutionInfo() {
-        return super.getResolutionInfo();
+        return getResolutionInfoInternal();
     }
 
     /**
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 bb46483..698221b 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
@@ -26,6 +26,7 @@
 import static androidx.camera.core.impl.utils.TransformUtils.within360;
 import static androidx.camera.core.processing.TargetUtils.isSuperset;
 import static androidx.core.util.Preconditions.checkArgument;
+import static androidx.core.util.Preconditions.checkArgumentInRange;
 
 import android.annotation.SuppressLint;
 import android.graphics.Matrix;
@@ -33,6 +34,7 @@
 import android.media.ImageReader;
 import android.util.Range;
 import android.util.Size;
+import android.view.OrientationEventListener;
 import android.view.Surface;
 
 import androidx.annotation.CallSuper;
@@ -60,7 +62,6 @@
 import androidx.camera.core.resolutionselector.ResolutionSelector;
 import androidx.camera.core.streamsharing.StreamSharing;
 import androidx.core.util.Preconditions;
-import androidx.lifecycle.LifecycleOwner;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -292,17 +293,64 @@
     }
 
     /**
-     * Converts orientation degrees to {@link Surface} rotation.
+     * A utility function that can convert the orientation degrees of
+     * {@link OrientationEventListener} to the nearest {@link Surface} rotation.
+     *
+     * <p>In general, it is best to use an {@link android.view.OrientationEventListener} to set
+     * the UseCase target rotation. This way, the rotation output will indicate which way is down
+     * for a given image or video. This is important since display orientation may be locked by
+     * device default, user setting, or app configuration, and some devices may not transition to a
+     * reverse-portrait display orientation. In these cases, set target rotation dynamically
+     * according to the {@link android.view.OrientationEventListener}, without re-creating the
+     * use case. The sample code is as below:
+     * <pre>{@code
+     * public class CameraXActivity extends AppCompatActivity {
+     *
+     *     private OrientationEventListener mOrientationEventListener;
+     *
+     *     @Override
+     *     protected void onStart() {
+     *         super.onStart();
+     *         if (mOrientationEventListener == null) {
+     *             mOrientationEventListener = new OrientationEventListener(this) {
+     *                 @Override
+     *                 public void onOrientationChanged(int orientation) {
+     *                     if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
+     *                         return;
+     *                     }
+     *                     int rotation = UseCase.snapToSurfaceRotation(orientation);
+     *                     mImageCapture.setTargetRotation(rotation);
+     *                     mImageAnalysis.setTargetRotation(rotation);
+     *                     mVideoCapture.setTargetRotation(rotation);
+     *                 }
+     *             };
+     *         }
+     *         mOrientationEventListener.enable();
+     *     }
+     *
+     *     @Override
+     *     protected void onStop() {
+     *         super.onStop();
+     *         mOrientationEventListener.disable();
+     *     }
+     * }
+     * }</pre>
+     *
+     * @param orientation the orientation degrees in range [0, 359].
+     * @return surface rotation. One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
+     * {@link Surface#ROTATION_180} and {@link Surface#ROTATION_270}.
+     * @throws IllegalArgumentException if the input orientation degrees is not in range [0, 359].
+     * @see ImageCapture#setTargetRotation(int)
+     * @see ImageAnalysis#setTargetRotation(int)
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
     @ImageOutputConfig.RotationValue
-    protected static int orientationDegreesToSurfaceRotation(int degrees) {
-        int degreesWithin360 = within360(degrees);
-        if (degreesWithin360 >= 315 || degreesWithin360 < 45) {
+    public static int snapToSurfaceRotation(@IntRange(from = 0, to = 359) int orientation) {
+        checkArgumentInRange(orientation, 0, 359, "orientation");
+        if (orientation >= 315 || orientation < 45) {
             return Surface.ROTATION_0;
-        } else if (degreesWithin360 >= 225) {
+        } else if (orientation >= 225) {
             return Surface.ROTATION_90;
-        } else if (degreesWithin360 >= 135) {
+        } else if (orientation >= 135) {
             return Surface.ROTATION_180;
         } else {
             return Surface.ROTATION_270;
@@ -891,23 +939,6 @@
     }
 
     /**
-     * Returns {@link ResolutionInfo} of the use case.
-     *
-     * <p>The resolution information might change if the use case is unbound and then rebound or
-     * the target rotation setting is changed. The application needs to call
-     * {@code getResolutionInfo()} again to get the latest {@link ResolutionInfo} for the changes.
-     *
-     * @return the resolution information if the use case has been bound by the
-     * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner
-     *, CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Nullable
-    public ResolutionInfo getResolutionInfo() {
-        return getResolutionInfoInternal();
-    }
-
-    /**
      * Returns a new {@link ResolutionInfo} according to the latest settings of the use case, or
      * null if the use case is not bound yet.
      *
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
index e0f9eda..a14dfdf 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
@@ -23,6 +23,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.graphics.ImageFormat;
 import android.media.ImageReader;
 import android.os.Build;
 import android.util.Size;
@@ -36,6 +37,7 @@
 import androidx.camera.core.ForwardingImageProxy;
 import androidx.camera.core.ImageCaptureException;
 import androidx.camera.core.ImageProxy;
+import androidx.camera.core.ImageReaderProxyProvider;
 import androidx.camera.core.ImageReaderProxys;
 import androidx.camera.core.Logger;
 import androidx.camera.core.MetadataImageReader;
@@ -95,13 +97,13 @@
                 "CaptureNode does not support recreation yet.");
         mInputEdge = inputEdge;
         Size size = inputEdge.getSize();
-        int format = inputEdge.getFormat();
+        int format = inputEdge.getInputFormat();
 
         // Create and configure ImageReader.
         Consumer<ProcessingRequest> requestConsumer;
         ImageReaderProxy wrappedImageReader;
         boolean hasMetadata = !inputEdge.isVirtualCamera();
-        if (hasMetadata) {
+        if (hasMetadata && inputEdge.getImageReaderProxyProvider() == null) {
             // Use MetadataImageReader if the input edge expects metadata.
             MetadataImageReader metadataImageReader = new MetadataImageReader(size.getWidth(),
                     size.getHeight(), format, MAX_IMAGES);
@@ -111,8 +113,8 @@
         } else {
             // Use NoMetadataImageReader if the input edge does not expect metadata.
             NoMetadataImageReader noMetadataImageReader = new NoMetadataImageReader(
-                    ImageReaderProxys.createIsolatedReader(
-                            size.getWidth(), size.getHeight(), format, MAX_IMAGES));
+                    createImageReaderProxy(inputEdge.getImageReaderProxyProvider(),
+                            size.getWidth(), size.getHeight(), format));
             wrappedImageReader = noMetadataImageReader;
             // Forward the request to the NoMetadataImageReader to create fake metadata.
             requestConsumer = request -> {
@@ -141,10 +143,22 @@
         inputEdge.getRequestEdge().setListener(requestConsumer);
         inputEdge.getErrorEdge().setListener(this::sendCaptureError);
 
-        mOutputEdge = Out.of(inputEdge.getFormat(), inputEdge.isVirtualCamera());
+        mOutputEdge = Out.of(inputEdge.getInputFormat(), inputEdge.getOutputFormat(),
+                inputEdge.isVirtualCamera());
         return mOutputEdge;
     }
 
+    @NonNull
+    private static ImageReaderProxy createImageReaderProxy(
+            @Nullable ImageReaderProxyProvider imageReaderProxyProvider, int width, int height,
+            int format) {
+        if (imageReaderProxyProvider != null) {
+            return imageReaderProxyProvider.newInstance(width, height, format, MAX_IMAGES, 0);
+        } else {
+            return ImageReaderProxys.createIsolatedReader(width, height, format, MAX_IMAGES);
+        }
+    }
+
     @VisibleForTesting
     @MainThread
     void onImageProxyAvailable(@NonNull ImageProxy imageProxy) {
@@ -282,9 +296,17 @@
         abstract Size getSize();
 
         /**
-         * Size of the {@link ImageReader} format.
+         * The input format of the pipeline. The format of the {@link ImageReader}.
          */
-        abstract int getFormat();
+        abstract int getInputFormat();
+
+        /**
+         * The output format of the pipeline.
+         *
+         * <p> For public users, only {@link ImageFormat#JPEG} is supported. Other formats are
+         * only used by in-memory capture in tests.
+         */
+        abstract int getOutputFormat();
 
         /**
          * Whether the pipeline is connected to a virtual camera.
@@ -292,6 +314,12 @@
         abstract boolean isVirtualCamera();
 
         /**
+         * Whether the pipeline is connected to a virtual camera.
+         */
+        @Nullable
+        abstract ImageReaderProxyProvider getImageReaderProxyProvider();
+
+        /**
          * Edge that accepts {@link ProcessingRequest}.
          */
         @NonNull
@@ -315,7 +343,7 @@
 
         void setSurface(@NonNull Surface surface) {
             checkState(mSurface == null, "The surface is already set.");
-            mSurface = new ImmediateSurface(surface, getSize(), getFormat());
+            mSurface = new ImmediateSurface(surface, getSize(), getInputFormat());
         }
 
         /**
@@ -333,9 +361,10 @@
         }
 
         @NonNull
-        static In of(Size size, int format, boolean isVirtualCamera) {
-            return new AutoValue_CaptureNode_In(size, format, isVirtualCamera,
-                    new Edge<>(), new Edge<>());
+        static In of(Size size, int inputFormat, int outputFormat, boolean isVirtualCamera,
+                @Nullable ImageReaderProxyProvider imageReaderProxyProvider) {
+            return new AutoValue_CaptureNode_In(size, inputFormat, outputFormat, isVirtualCamera,
+                    imageReaderProxyProvider, new Edge<>(), new Edge<>());
         }
     }
 
@@ -360,16 +389,24 @@
         /**
          * Format of the {@link ImageProxy} in {@link #getImageEdge()}.
          */
-        abstract int getFormat();
+        abstract int getInputFormat();
+
+        /**
+         * Output format of the pipeline.
+         *
+         * <p> For public users, only {@link ImageFormat#JPEG} is supported. Other formats are
+         * only used by in-memory capture in tests.
+         */
+        abstract int getOutputFormat();
 
         /**
          * Whether the pipeline is connected to a virtual camera.
          */
         abstract boolean isVirtualCamera();
 
-        static Out of(int format, boolean isVirtualCamera) {
-            return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), format,
-                    isVirtualCamera);
+        static Out of(int inputFormat, int outputFormat, boolean isVirtualCamera) {
+            return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), inputFormat,
+                    outputFormat, isVirtualCamera);
         }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
index 13b05cf..7b33672 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
@@ -17,6 +17,7 @@
 package androidx.camera.core.imagecapture;
 
 import static androidx.camera.core.CaptureBundles.singleDefaultCaptureBundle;
+import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_BUFFER_FORMAT;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
 import static androidx.camera.core.impl.utils.TransformUtils.hasCropping;
 
@@ -111,8 +112,12 @@
                 cameraEffect != null ? new InternalImageProcessor(cameraEffect) : null);
 
         // Connect nodes
-        mPipelineIn = CaptureNode.In.of(cameraSurfaceSize, mUseCaseConfig.getInputFormat(),
-                isVirtualCamera);
+        mPipelineIn = CaptureNode.In.of(
+                cameraSurfaceSize,
+                mUseCaseConfig.getInputFormat(),
+                getOutputFormat(),
+                isVirtualCamera,
+                mUseCaseConfig.getImageReaderProxyProvider());
         CaptureNode.Out captureOut = mCaptureNode.transform(mPipelineIn);
         ProcessingNode.In processingIn = mBundlingNode.transform(captureOut);
         mProcessingNode.transform(processingIn);
@@ -211,6 +216,16 @@
 
     // ===== private methods =====
 
+    private int getOutputFormat() {
+        Integer bufferFormat = mUseCaseConfig.retrieveOption(OPTION_BUFFER_FORMAT, null);
+        // Return the buffer format if it is set.
+        if (bufferFormat != null) {
+            return bufferFormat;
+        }
+        // By default, use JPEG format.
+        return ImageFormat.JPEG;
+    }
+
     @NonNull
     private CaptureBundle createCaptureBundle() {
         return requireNonNull(mUseCaseConfig.getCaptureBundle(singleDefaultCaptureBundle()));
@@ -251,7 +266,7 @@
 
             // Only sets the JPEG rotation and quality for JPEG format. Some devices do not
             // handle these configs for non-JPEG images. See b/204375890.
-            if (mPipelineIn.getFormat() == ImageFormat.JPEG) {
+            if (mPipelineIn.getInputFormat() == ImageFormat.JPEG) {
                 if (EXIF_ROTATION_AVAILABILITY.isRotationOptionSupported()) {
                     builder.addImplementationOption(CaptureConfig.OPTION_ROTATION,
                             takePictureRequest.getRotationDegrees());
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
index f53cae3..ba7717d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
@@ -16,10 +16,12 @@
 
 package androidx.camera.core.imagecapture;
 
+import static android.graphics.ImageFormat.JPEG;
 import static android.graphics.ImageFormat.YUV_420_888;
 
 import static androidx.camera.core.ImageCapture.ERROR_UNKNOWN;
 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
+import static androidx.core.util.Preconditions.checkArgument;
 import static androidx.core.util.Preconditions.checkState;
 
 import static java.util.Objects.requireNonNull;
@@ -63,6 +65,7 @@
     @Nullable
     final InternalImageProcessor mImageProcessor;
 
+    private ProcessingNode.In mInputEdge;
     private Operation<InputPacket, Packet<ImageProxy>> mInput2Packet;
     private Operation<Image2JpegBytes.In, Packet<byte[]>> mImage2JpegBytes;
     private Operation<Bitmap2JpegBytes.In, Packet<byte[]>> mBitmap2JpegBytes;
@@ -100,6 +103,7 @@
     @NonNull
     @Override
     public Void transform(@NonNull ProcessingNode.In inputEdge) {
+        mInputEdge = inputEdge;
         // Listen to the input edge.
         inputEdge.getEdge().setListener(
                 inputPacket -> {
@@ -116,7 +120,7 @@
         mBitmap2JpegBytes = new Bitmap2JpegBytes();
         mJpegBytes2Disk = new JpegBytes2Disk();
         mJpegImage2Result = new JpegImage2Result();
-        if (inputEdge.getFormat() == YUV_420_888 || mImageProcessor != null) {
+        if (inputEdge.getInputFormat() == YUV_420_888 || mImageProcessor != null) {
             // Convert JPEG bytes to ImageProxy for:
             // - YUV input: YUV -> JPEG -> ImageProxy
             // - Effects: JPEG -> Bitmap -> effect -> Bitmap -> JPEG -> ImageProxy
@@ -162,6 +166,9 @@
     @WorkerThread
     ImageCapture.OutputFileResults processOnDiskCapture(@NonNull InputPacket inputPacket)
             throws ImageCaptureException {
+        checkArgument(mInputEdge.getOutputFormat() == JPEG,
+                String.format("On-disk capture only support JPEG output format. Output format: %s",
+                        mInputEdge.getOutputFormat()));
         ProcessingRequest request = inputPacket.getProcessingRequest();
         Packet<ImageProxy> originalImage = mInput2Packet.apply(inputPacket);
         Packet<byte[]> jpegBytes = mImage2JpegBytes.apply(
@@ -179,7 +186,8 @@
             throws ImageCaptureException {
         ProcessingRequest request = inputPacket.getProcessingRequest();
         Packet<ImageProxy> image = mInput2Packet.apply(inputPacket);
-        if (image.getFormat() == YUV_420_888 || mBitmapEffect != null) {
+        if ((image.getFormat() == YUV_420_888 || mBitmapEffect != null)
+                && mInputEdge.getOutputFormat() == JPEG) {
             Packet<byte[]> jpegBytes = mImage2JpegBytes.apply(
                     Image2JpegBytes.In.of(image, request.getJpegQuality()));
             if (mBitmapEffect != null) {
@@ -195,7 +203,7 @@
      */
     private Packet<byte[]> cropAndMaybeApplyEffect(Packet<byte[]> jpegPacket, int jpegQuality)
             throws ImageCaptureException {
-        checkState(jpegPacket.getFormat() == ImageFormat.JPEG);
+        checkState(jpegPacket.getFormat() == JPEG);
         Packet<Bitmap> bitmapPacket = mJpegBytes2CroppedBitmap.apply(jpegPacket);
         if (mBitmapEffect != null) {
             // Apply effect if present.
@@ -251,10 +259,18 @@
         /**
          * Gets the format of the image in {@link InputPacket}.
          */
-        abstract int getFormat();
+        abstract int getInputFormat();
 
-        static In of(int format) {
-            return new AutoValue_ProcessingNode_In(new Edge<>(), format);
+        /**
+         * The output format of the pipeline.
+         *
+         * <p> For public users, only {@link ImageFormat#JPEG} is supported. Other formats are
+         * only used by in-memory capture in tests.
+         */
+        abstract int getOutputFormat();
+
+        static In of(int inputFormat, int outputFormat) {
+            return new AutoValue_ProcessingNode_In(new Edge<>(), inputFormat, outputFormat);
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
index 1af04d6..f9d7238 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
@@ -53,7 +53,8 @@
         captureNodeOut.getImageEdge().setListener(this::matchImageWithRequest);
         captureNodeOut.getRequestEdge().setListener(this::trackIncomingRequest);
         // Set up output edge.
-        mOutputEdge = ProcessingNode.In.of(captureNodeOut.getFormat());
+        mOutputEdge = ProcessingNode.In.of(captureNodeOut.getInputFormat(),
+                captureNodeOut.getOutputFormat());
         return mOutputEdge;
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
index 803bfed..58deea49 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
@@ -27,6 +27,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Logger;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.Futures;
@@ -331,7 +332,7 @@
         return mPrescribedStreamFormat;
     }
 
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public int getUseCount() {
         synchronized (mLock) {
             return mUseCount;
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 8662d08b..91b520a 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
@@ -27,6 +27,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.Logger;
 import androidx.camera.core.internal.compat.workaround.SurfaceSorter;
 
@@ -110,6 +111,17 @@
         public abstract int getSurfaceGroupId();
 
         /**
+         * Returns the dynamic range for this output configuration.
+         *
+         * <p>The dynamic range will determine the dynamic range format and profile of pixels in
+         * the surfaces associated with this output configuration.
+         *
+         * <p>If not set, this defaults to {@link DynamicRange#SDR}.
+         */
+        @NonNull
+        public abstract DynamicRange getDynamicRange();
+
+        /**
          * Creates the {@link Builder} instance with specified {@link DeferrableSurface}.
          */
         @NonNull
@@ -118,7 +130,8 @@
                     .setSurface(surface)
                     .setSharedSurfaces(Collections.emptyList())
                     .setPhysicalCameraId(null)
-                    .setSurfaceGroupId(SURFACE_GROUP_ID_NONE);
+                    .setSurfaceGroupId(SURFACE_GROUP_ID_NONE)
+                    .setDynamicRange(DynamicRange.SDR);
         }
 
         /**
@@ -157,6 +170,15 @@
             public abstract Builder setSurfaceGroupId(int surfaceGroupId);
 
             /**
+             * Returns the dynamic range for this output configuration.
+             *
+             * <p>The dynamic range will determine the dynamic range format and profile of pixels in
+             * the surfaces associated with this output configuration.
+             */
+            @NonNull
+            public abstract Builder setDynamicRange(@NonNull DynamicRange dynamicRange);
+
+            /**
              * Creates the instance.
              */
             @NonNull
@@ -558,10 +580,27 @@
         }
 
 
-        /** Add a surface to the set that the session repeatedly writes data to. */
+        /**
+         * Add a surface to the set that the session repeatedly writes data to.
+         *
+         * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
+         * manually set the dynamic range, use {@link #addSurface(DeferrableSurface, DynamicRange)}.
+         */
         @NonNull
         public Builder addSurface(@NonNull DeferrableSurface surface) {
-            OutputConfig outputConfig = OutputConfig.builder(surface).build();
+            return addSurface(surface, DynamicRange.SDR);
+        }
+
+        /**
+         * Add a surface with the provided dynamic range to the set that the session repeatedly
+         * writes data to.
+         */
+        @NonNull
+        public Builder addSurface(@NonNull DeferrableSurface surface,
+                @NonNull DynamicRange dynamicRange) {
+            OutputConfig outputConfig = OutputConfig.builder(surface)
+                    .setDynamicRange(dynamicRange)
+                    .build();
             mOutputConfigs.add(outputConfig);
             mCaptureConfigBuilder.addSurface(surface);
             return this;
@@ -581,10 +620,28 @@
             return this;
         }
 
-        /** Add a surface for the session which only used for single captures. */
+        /**
+         * Add a surface for the session which only used for single captures.
+         *
+         * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
+         * manually set the dynamic range, use
+         * {@link #addNonRepeatingSurface(DeferrableSurface, DynamicRange)}.
+         */
         @NonNull
         public Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface) {
-            OutputConfig outputConfig = OutputConfig.builder(surface).build();
+            return addNonRepeatingSurface(surface, DynamicRange.SDR);
+        }
+
+        /**
+         * Add a surface with the provided dynamic range for the session which only used for
+         * single captures.
+         */
+        @NonNull
+        public Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface,
+                @NonNull DynamicRange dynamicRange) {
+            OutputConfig outputConfig = OutputConfig.builder(surface)
+                    .setDynamicRange(dynamicRange)
+                    .build();
             mOutputConfigs.add(outputConfig);
             return this;
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
index 74389f1..d9c51b0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
@@ -34,7 +34,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.CameraEffect;
 import androidx.camera.core.Logger;
@@ -239,7 +238,7 @@
     /**
      * Returns the close state.
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     public boolean isClosed() {
         synchronized (mLock) {
             return mIsClosed;
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 682887e..3195cae 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
@@ -163,23 +163,6 @@
     }
 
     @Test
-    public void setTargetRotationDegrees() {
-        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
-        imageAnalysis.setTargetRotationDegrees(45);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_270);
-        imageAnalysis.setTargetRotationDegrees(135);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
-        imageAnalysis.setTargetRotationDegrees(225);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_90);
-        imageAnalysis.setTargetRotationDegrees(315);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_0);
-        imageAnalysis.setTargetRotationDegrees(405);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_270);
-        imageAnalysis.setTargetRotationDegrees(-45);
-        assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_0);
-    }
-
-    @Test
     public void defaultMirrorModeIsOff() {
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
         assertThat(imageAnalysis.getMirrorModeInternal()).isEqualTo(MIRROR_MODE_OFF);
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 5581da2..ad923b7f1 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
@@ -33,8 +33,8 @@
 import androidx.camera.core.ImageCapture.ImageCaptureRequest
 import androidx.camera.core.ImageCapture.ImageCaptureRequestProcessor
 import androidx.camera.core.ImageCapture.ImageCaptureRequestProcessor.ImageCaptor
-import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.impl.CameraConfig
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CaptureConfig
@@ -45,6 +45,7 @@
 import androidx.camera.core.impl.TagBundle
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.core.internal.utils.SizeUtil
@@ -183,23 +184,6 @@
     }
 
     @Test
-    fun setTargetRotationDegrees() {
-        val imageCapture = ImageCapture.Builder().build()
-        imageCapture.setTargetRotationDegrees(45)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_270)
-        imageCapture.setTargetRotationDegrees(135)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_180)
-        imageCapture.setTargetRotationDegrees(225)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_90)
-        imageCapture.setTargetRotationDegrees(315)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_0)
-        imageCapture.setTargetRotationDegrees(405)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_270)
-        imageCapture.setTargetRotationDegrees(-45)
-        assertThat(imageCapture.targetRotation).isEqualTo(Surface.ROTATION_0)
-    }
-
-    @Test
     fun defaultMirrorModeIsOff() {
         val imageCapture = ImageCapture.Builder().build()
         assertThat(imageCapture.mirrorModeInternal).isEqualTo(MIRROR_MODE_OFF)
@@ -306,24 +290,24 @@
     }
 
     @Test
-    fun useImageReaderProvider_pipelineDisabled() {
+    fun useImageReaderProvider_pipelineEnabled() {
         assertThat(
             bindImageCapture(
                 useProcessingPipeline = true,
                 bufferFormat = ImageFormat.JPEG,
                 imageReaderProxyProvider = getImageReaderProxyProvider(),
             ).isProcessingPipelineEnabled
-        ).isFalse()
+        ).isTrue()
     }
 
     @Test
-    fun yuvFormat_pipelineDisabled() {
+    fun yuvFormat_pipelineEnabled() {
         assertThat(
             bindImageCapture(
                 useProcessingPipeline = true,
                 bufferFormat = ImageFormat.YUV_420_888,
             ).isProcessingPipelineEnabled
-        ).isFalse()
+        ).isTrue()
     }
 
     @Config(minSdk = 28)
@@ -348,11 +332,10 @@
         )
 
         // Act
-        imageCapture.takePicture(executor, onImageCapturedCallback)
+        imageCapture.takePicture(mainThreadExecutor(), onImageCapturedCallback)
         // Send fake image.
         fakeImageReaderProxy?.triggerImageAvailable(TagBundle.create(Pair("TagBundleKey", 0)), 0)
         shadowOf(getMainLooper()).idle()
-        flushHandler(callbackHandler)
 
         // Assert.
         // The expected value is based on fitting the 1:1 view port into a rect with the size of
@@ -398,11 +381,10 @@
         )
 
         // Act
-        imageCapture.takePicture(executor, onImageCapturedCallback)
+        imageCapture.takePicture(mainThreadExecutor(), onImageCapturedCallback)
         // Send fake image.
         fakeImageReaderProxy?.triggerImageAvailable(TagBundle.create(Pair("TagBundleKey", 0)), 0)
         shadowOf(getMainLooper()).idle()
-        flushHandler(callbackHandler)
 
         // Assert.
         assertThat(capturedImage!!.width).isEqualTo(fakeImageReaderProxy?.width)
@@ -734,6 +716,7 @@
             .setTargetRotation(Surface.ROTATION_0)
             .setCaptureMode(captureMode)
             .setFlashMode(ImageCapture.FLASH_MODE_OFF)
+            .setIoExecutor(mainThreadExecutor())
             .setCaptureOptionUnpacker { _: UseCaseConfig<*>?, _: CaptureConfig.Builder? -> }
             .setSessionOptionUnpacker { _: Size, _: UseCaseConfig<*>?,
                 _: SessionConfig.Builder? ->
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
index acc1781..ffcd64c 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
@@ -16,14 +16,16 @@
 
 package androidx.camera.core.imagecapture
 
-import android.graphics.ImageFormat
+import android.graphics.ImageFormat.JPEG
 import android.os.Build
 import android.os.Looper.getMainLooper
 import android.util.Size
 import androidx.camera.core.ImageProxy
+import androidx.camera.core.ImageReaderProxyProvider
 import androidx.camera.core.imagecapture.Utils.createCaptureBundle
 import androidx.camera.core.imagecapture.Utils.createFakeImage
 import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.testing.fakes.FakeImageReaderProxy
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -52,7 +54,7 @@
 
     @Before
     fun setUp() {
-        captureNodeIn = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG, false)
+        captureNodeIn = CaptureNode.In.of(Size(10, 10), JPEG, JPEG, false, null)
         captureNodeOut = captureNode.transform(captureNodeIn)
         captureNodeOut.imageEdge.setListener {
             imagePropagated.add(it)
@@ -68,6 +70,22 @@
     }
 
     @Test
+    fun hasImageReaderProxyProvider_useTheProvidedImageReader() {
+        // Arrange: create a fake ImageReaderProxyProvider.
+        val imageReader = FakeImageReaderProxy(CaptureNode.MAX_IMAGES)
+        val imageReaderProvider = ImageReaderProxyProvider { _, _, _, _, _ ->
+            imageReader
+        }
+        val input = CaptureNode.In.of(Size(10, 10), JPEG, JPEG, false, imageReaderProvider)
+        // Act: transform.
+        val node = CaptureNode()
+        node.transform(input)
+        // Assert: ImageReaderProxyProvider is used.
+        assertThat(input.surface.surface.get()).isEqualTo(imageReader.surface)
+        node.release()
+    }
+
+    @Test
     fun release_imageReaderNotClosedUntilTermination() {
         // Arrange: increment the DeferrableSurface's use count to prevent it from being terminated.
         captureNode.inputEdge.surface.incrementUseCount()
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
index de40c35..34b1b4a 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
@@ -28,6 +28,7 @@
 import androidx.camera.core.ImageCapture.CaptureMode
 import androidx.camera.core.ImageCaptureException
 import androidx.camera.core.ImageProxy
+import androidx.camera.core.ImageReaderProxyProvider
 import androidx.camera.core.SafeCloseImageReaderProxy
 import androidx.camera.core.imagecapture.CaptureNode.MAX_IMAGES
 import androidx.camera.core.imagecapture.ImagePipeline.JPEG_QUALITY_MAX_QUALITY
@@ -46,6 +47,7 @@
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.CaptureConfig.OPTION_ROTATION
 import androidx.camera.core.impl.ImageCaptureConfig
+import androidx.camera.core.impl.ImageCaptureConfig.OPTION_BUFFER_FORMAT
 import androidx.camera.core.impl.ImageInputConfig
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
@@ -53,6 +55,7 @@
 import androidx.camera.core.processing.Packet
 import androidx.camera.testing.TestImageUtil.createJpegBytes
 import androidx.camera.testing.TestImageUtil.createJpegFakeImageProxy
+import androidx.camera.testing.TestImageUtil.createYuvFakeImageProxy
 import androidx.camera.testing.fakes.FakeCameraCaptureResult
 import androidx.camera.testing.fakes.FakeImageInfo
 import androidx.camera.testing.fakes.FakeImageReaderProxy
@@ -89,9 +92,10 @@
     @Before
     fun setUp() {
         // Create ImageCaptureConfig.
-        val builder = ImageCapture.Builder().setCaptureOptionUnpacker { _, builder ->
-            builder.templateType = TEMPLATE_TYPE
-        }
+        val builder = ImageCapture.Builder()
+            .setCaptureOptionUnpacker { _, builder ->
+                builder.templateType = TEMPLATE_TYPE
+            }
         builder.mutableConfig.insertOption(OPTION_IO_EXECUTOR, mainThreadExecutor())
         builder.mutableConfig.insertOption(ImageInputConfig.OPTION_INPUT_FORMAT, ImageFormat.JPEG)
         imageCaptureConfig = builder.useCaseConfig
@@ -104,6 +108,31 @@
     }
 
     @Test
+    fun createPipeline_captureNodeHasImageReaderProxyProvider() {
+        // Arrange.
+        val imageReaderProxyProvider = ImageReaderProxyProvider { _, _, _, _, _ ->
+            FakeImageReaderProxy(MAX_IMAGES)
+        }
+        val builder = ImageCapture.Builder()
+            .setImageReaderProxyProvider(imageReaderProxyProvider)
+            .setCaptureOptionUnpacker { _, builder ->
+                builder.templateType = TEMPLATE_TYPE
+            }
+        builder.mutableConfig.insertOption(ImageInputConfig.OPTION_INPUT_FORMAT, ImageFormat.JPEG)
+        // Act.
+        val pipeline = ImagePipeline(builder.useCaseConfig, SIZE)
+        // Assert.
+        assertThat(pipeline.captureNode.inputEdge.imageReaderProxyProvider).isEqualTo(
+            imageReaderProxyProvider
+        )
+    }
+
+    @Test
+    fun createPipelineWithoutImageReaderProxyProvider_isNull() {
+        assertThat(imagePipeline.captureNode.inputEdge.imageReaderProxyProvider).isNull()
+    }
+
+    @Test
     fun createPipelineWithVirtualCamera_receivesImageProxy() {
         // Arrange: close the pipeline and create a new one not expecting metadata.
         imagePipeline.close()
@@ -309,6 +338,34 @@
     }
 
     @Test
+    fun createPipelineWithYuvOutput_getsYuvImage() {
+        val builder = ImageCapture.Builder().setCaptureOptionUnpacker { _, builder ->
+            builder.templateType = TEMPLATE_TYPE
+        }
+        builder.mutableConfig.insertOption(OPTION_BUFFER_FORMAT, ImageFormat.YUV_420_888)
+        builder.mutableConfig.insertOption(OPTION_IO_EXECUTOR, mainThreadExecutor())
+        builder.mutableConfig.insertOption(ImageInputConfig.OPTION_INPUT_FORMAT, ImageFormat.JPEG)
+        val pipeline = ImagePipeline(builder.useCaseConfig, SIZE)
+
+        // Arrange.
+        val processingRequest = imagePipeline.createRequests(
+            IN_MEMORY_REQUEST, CALLBACK, Futures.immediateFuture(null)
+        ).second!!
+        val imageInfo = createCameraCaptureResultImageInfo(
+            processingRequest.tagBundleKey,
+            processingRequest.stageIds.single()
+        )
+        val image = createYuvFakeImageProxy(imageInfo, WIDTH, HEIGHT)
+
+        // Act: send processing request and the image.
+        pipeline.submitProcessingRequest(processingRequest)
+        pipeline.captureNode.onImageProxyAvailable(image)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(CALLBACK.inMemoryResult!!.format).isEqualTo(ImageFormat.YUV_420_888)
+    }
+
+    @Test
     fun sendInMemoryRequest_receivesImageProxy() {
         // Arrange & act.
         val image = sendInMemoryRequest(imagePipeline)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
index 0234bed8..8710e8b 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
@@ -58,7 +58,7 @@
 
     @Before
     fun setUp() {
-        processingNodeIn = ProcessingNode.In.of(ImageFormat.JPEG)
+        processingNodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
         node.transform(processingNodeIn)
     }
 
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
index e04e4c4..94b0c42 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
@@ -44,7 +44,7 @@
 
     @Before
     fun setUp() {
-        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, false)
+        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, ImageFormat.JPEG, false)
         matchingNodeOut = node.transform(captureNodeOut)
         matchingNodeOut.edge.setListener {
             packetPropagated.add(it)
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 942e44b..1627bec 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -58,6 +58,7 @@
     androidTestImplementation(project(":camera:camera-video"))
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation(project(":camera:camera-testlib-extensions"))
+    androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     // To use the testlib to have the implementation of the extensions-stub interface.
 }
 
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
index 78fa363..f9e1b5a 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
@@ -72,6 +72,7 @@
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.SurfaceTextureProvider
 import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.concurrent.futures.await
 import androidx.lifecycle.Observer
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.LargeTest
@@ -549,7 +550,7 @@
         }
     }
 
-    private fun initBasicExtenderSessionProcessor(): AutoCloseable {
+    private suspend fun initBasicExtenderSessionProcessor(): AutoCloseable {
         val width = 640
         val height = 480
         val maxImages = 2
@@ -557,9 +558,9 @@
         val handlerThread = HandlerThread("CameraX-AutoDrainThread")
         handlerThread.start()
         val handler = Handler(handlerThread.looper)
-        val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTexture(
-            CameraXExecutors.newHandlerExecutor(handler), width, height
-        ) {}
+        val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
+            CameraXExecutors.newHandlerExecutor(handler), width, height, null
+        ) { handlerThread.quitSafely() }.await()
         val previewOutputSurface = OutputSurface.create(
             Surface(surfaceTextureHolder.surfaceTexture),
             Size(width, height),
@@ -583,7 +584,6 @@
         return AutoCloseable {
             jpegImageReader.close()
             surfaceTextureHolder.close()
-            handlerThread.quitSafely()
         }
     }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index 3672fcd..b6c7ced 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -24,7 +24,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.CameraProvider;
 import androidx.camera.core.CameraSelector;
@@ -277,7 +276,7 @@
      */
     // TODO: Will need to be rewritten to be threadsafe with use in conjunction with
     //  ExtensionsManager.init(...) if this is to be released for use outside of testing.
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public ListenableFuture<Void> shutdown() {
         synchronized (EXTENSIONS_LOCK) {
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 286acb9..7909211 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
@@ -36,8 +36,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraEffect;
 import androidx.camera.core.CameraFilter;
@@ -283,7 +282,7 @@
      * future is a no-op.
      * @hide
      */
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public ListenableFuture<Void> shutdown() {
         runOnMainSync(this::unbindAll);
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
index 198e8c24..832c091 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -50,7 +50,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CameraXConfig;
@@ -556,7 +556,7 @@
      * @param cameraCoordinator The camera coordinator for concurrent cameras.
      * @param cameraSelector The selector to select cameras with.
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public static CameraUseCaseAdapter createCameraUseCaseAdapter(
             @NonNull Context context,
@@ -591,7 +591,7 @@
      * @param context        The context used to initialize CameraX
      * @param cameraSelector The selector to select cameras with.
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public static CameraUseCaseAdapter createCameraUseCaseAdapter(
             @NonNull Context context,
@@ -616,7 +616,7 @@
      * @param cameraSelector The selector to select cameras with.
      * @param useCases       The UseCases to attach to the CameraUseCaseAdapter.
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public static CameraUseCaseAdapter createCameraAndAttachUseCase(
             @NonNull Context context,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
index 8387b97..72c6e2f 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
@@ -35,8 +35,11 @@
 import androidx.camera.core.Logger;
 import androidx.camera.core.Preview;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.os.HandlerCompat;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -107,7 +110,6 @@
      * a surface for preview.
      *
      * <p> The {@link SurfaceTexture} will be released when it is no longer needed.
-     *
      */
     @NonNull
     public static Preview.SurfaceProvider createSurfaceTextureProvider() {
@@ -154,72 +156,81 @@
             handlerThread.start();
             Handler handler = HandlerCompat.createAsync(handlerThread.getLooper());
             Executor glContextExecutor = CameraXExecutors.newHandlerExecutor(handler);
-            glContextExecutor.execute(() -> {
+            ListenableFuture<SurfaceTextureHolder> surfaceTextureFuture =
+                    createAutoDrainingSurfaceTextureAsync(glContextExecutor,
+                            surfaceRequest.getResolution().getWidth(),
+                            surfaceRequest.getResolution().getHeight(), frameAvailableListener,
+                            handlerThread::quitSafely);
+
+            surfaceTextureFuture.addListener(() -> {
+                try {
+                    SurfaceTextureHolder holder = surfaceTextureFuture.get();
+                    surfaceRequest.provideSurface(new Surface(holder.getSurfaceTexture()),
+                            glContextExecutor,
+                            (surfaceResponse) -> {
+                                try {
+                                    holder.close();
+                                } catch (Exception e) {
+                                    throw new AssertionError("SurfaceTextureHolder failed"
+                                            + " to close", e);
+                                }
+                            });
+                } catch (Exception e) {
+                    // Should never happen
+                    throw new AssertionError("Failed to create auto-draining surface "
+                            + "texture",
+                            e);
+                }
+            }, glContextExecutor);
+        };
+    }
+
+    /**
+     * Creates a {@link SurfaceTextureHolder} asynchronously that contains a {@link SurfaceTexture}
+     * which will automatically drain frames as new frames arrive.
+     *
+     * @param glExecutor             the executor where the GL codes will run.
+     * @param width                  the width of the SurfaceTexture size
+     * @param height                 the height of the SurfaceTexture size.
+     * @param frameAvailableListener listener to be invoked when there are new frames.
+     * @param onClosed               runnable which will be called after all resources managed by
+     *                               the SurfaceTextureHolder have been released.
+     */
+    @NonNull
+    public static ListenableFuture<SurfaceTextureHolder> createAutoDrainingSurfaceTextureAsync(
+            @NonNull Executor glExecutor,
+            int width,
+            int height,
+            @Nullable SurfaceTexture.OnFrameAvailableListener frameAvailableListener,
+            @Nullable Runnable onClosed) {
+        return CallbackToFutureAdapter.getFuture((completer) -> {
+            glExecutor.execute(() -> {
                 EGLContextParams contextParams = createDummyEGLContext();
                 EGL14.eglMakeCurrent(contextParams.display, contextParams.outputSurface,
                         contextParams.outputSurface, contextParams.context);
                 int[] textureIds = new int[1];
                 GLES20.glGenTextures(1, textureIds, 0);
                 SurfaceTexture surfaceTexture = new SurfaceTexture(textureIds[0]);
-                surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(),
-                        surfaceRequest.getResolution().getHeight());
-                surfaceTexture.setOnFrameAvailableListener((st) -> {
-                    st.updateTexImage();
-                    if (frameAvailableListener != null) {
-                        frameAvailableListener.onFrameAvailable(st);
-                    }
-                }, handler);
+                surfaceTexture.setDefaultBufferSize(width, height);
+                surfaceTexture.setOnFrameAvailableListener(it ->
+                        glExecutor.execute(() -> {
+                            it.updateTexImage();
+                            if (frameAvailableListener != null) {
+                                frameAvailableListener.onFrameAvailable(surfaceTexture);
+                            }
+                        }));
 
-                Surface surface = new Surface(surfaceTexture);
-                surfaceRequest.provideSurface(surface,
-                        glContextExecutor,
-                        (surfaceResponse) -> {
-                            surface.release();
+                completer.set(
+                        new SurfaceTextureHolder(surfaceTexture, () -> glExecutor.execute(() -> {
                             surfaceTexture.release();
                             GLES20.glDeleteTextures(1, textureIds, 0);
                             terminateEGLContext(contextParams);
-                            handlerThread.quitSafely();
-                        });
+                            if (onClosed != null) {
+                                onClosed.run();
+                            }
+                        })));
             });
-        };
-    }
-
-    /**
-     * Creates a {@link SurfaceTextureHolder} that contains a {@link SurfaceTexture} which will
-     * automatically drain frames as new frames arrive.
-     *
-     * @param glExecutor             the executor where the GL codes will run.
-     * @param width                  the width of the SurfaceTexture size
-     * @param height                 the height of the SurfaceTexture size.
-     * @param frameAvailableListener listener to be invoked when there are new frames.
-     */
-    @NonNull
-    public static SurfaceTextureHolder createAutoDrainingSurfaceTexture(
-            @NonNull Executor glExecutor,
-            int width,
-            int height,
-            @NonNull SurfaceTexture.OnFrameAvailableListener frameAvailableListener) {
-        int[] textureIds = new int[1];
-        SurfaceTexture surfaceTexture = new SurfaceTexture(textureIds[0]);
-        EGLContextParams contextParams = createDummyEGLContext();
-        glExecutor.execute(() -> {
-            EGL14.eglMakeCurrent(contextParams.display, contextParams.outputSurface,
-                    contextParams.outputSurface, contextParams.context);
-            GLES20.glGenTextures(1, textureIds, 0);
-            surfaceTexture.setDefaultBufferSize(width, height);
-            surfaceTexture.setOnFrameAvailableListener(it ->
-                    glExecutor.execute(() -> {
-                        it.updateTexImage();
-                        frameAvailableListener.onFrameAvailable(surfaceTexture);
-                    }));
-        });
-
-        return new SurfaceTextureHolder(surfaceTexture, () -> {
-            glExecutor.execute(() -> {
-                surfaceTexture.release();
-                GLES20.glDeleteTextures(1, textureIds, 0);
-                terminateEGLContext(contextParams);
-            });
+            return "createAutoDrainingSurfaceTexture";
         });
     }
 
diff --git a/camera/camera-video/api/current.txt b/camera/camera-video/api/current.txt
index 7611bce..f1af41e 100644
--- a/camera/camera-video/api/current.txt
+++ b/camera/camera-video/api/current.txt
@@ -139,7 +139,7 @@
     method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public static <T extends androidx.camera.video.VideoOutput> androidx.camera.video.VideoCapture<T!> withOutput(T);
   }
 
diff --git a/camera/camera-video/api/public_plus_experimental_current.txt b/camera/camera-video/api/public_plus_experimental_current.txt
index 7611bce..f1af41e 100644
--- a/camera/camera-video/api/public_plus_experimental_current.txt
+++ b/camera/camera-video/api/public_plus_experimental_current.txt
@@ -139,7 +139,7 @@
     method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public static <T extends androidx.camera.video.VideoOutput> androidx.camera.video.VideoCapture<T!> withOutput(T);
   }
 
diff --git a/camera/camera-video/api/restricted_current.txt b/camera/camera-video/api/restricted_current.txt
index 7611bce..f1af41e 100644
--- a/camera/camera-video/api/restricted_current.txt
+++ b/camera/camera-video/api/restricted_current.txt
@@ -139,7 +139,7 @@
     method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
-    method public void setTargetRotationDegrees(int);
+    method @Deprecated public void setTargetRotationDegrees(int);
     method public static <T extends androidx.camera.video.VideoOutput> androidx.camera.video.VideoCapture<T!> withOutput(T);
   }
 
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
index 7ee2e0b..9cfc292 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.media.MediaCodec
 import android.media.MediaCodecInfo
+import android.os.Build
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.core.CameraSelector
@@ -118,13 +119,19 @@
 
             // Act.
             val (width, height) = videoProfile.width to videoProfile.height
+            // Pass if VideoCapabilities.isSizeSupported() is true
+            if (capabilities.isSizeSupported(width, height)) {
+                return@forEach
+            }
+
             val supportedWidths = capabilities.supportedWidths
             val supportedHeights = capabilities.supportedHeights
             val supportedWidthsForHeight = capabilities.getWidthsForHeightQuietly(height)
             val supportedHeightForWidth = capabilities.getHeightsForWidthQuietly(width)
 
             // Assert.
-            val msg = "mime: $mime, size: ${width}x$height is not in " +
+            val msg = "Build.BRAND: ${Build.BRAND}, Build.MODEL: ${Build.MODEL} " +
+                "mime: $mime, size: ${width}x$height is not in " +
                 "supported widths $supportedWidths/$supportedWidthsForHeight " +
                 "or heights $supportedHeights/$supportedHeightForWidth, " +
                 "the width/height alignment is " +
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 7a13541..892b739 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
@@ -222,6 +222,8 @@
 
     @Test
     fun addUseCases_setSupportedQuality_getCorrectResolution() = runBlocking {
+        assumeExtraCroppingQuirk(implName)
+
         val videoCapabilities = createFakeVideoCapabilities(supportedResolutionMap)
         assumeTrue(videoCapabilities.getSupportedQualities(dynamicRange).isNotEmpty())
         // Cuttlefish API 29 has inconsistent resolution issue. See b/184015059.
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 ef0d4dc..2a79dbd 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
@@ -32,6 +32,7 @@
 import android.util.Rational
 import android.util.Size
 import android.view.Surface
+import androidx.annotation.RequiresApi
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.core.AspectRatio
@@ -1063,13 +1064,7 @@
     }
 
     private fun assumeExtraCroppingQuirk() {
-        val msg =
-            "Devices in ExtraCroppingQuirk will get a fixed resolution regardless of any settings"
-        if (implName.contains(CameraPipeConfig::class.simpleName!!)) {
-            assumeTrue(msg, PipeDeviceQuirks[PipeExtraCroppingQuirk::class.java] == null)
-        } else {
-            assumeTrue(msg, Camera2DeviceQuirks.get(Camera2ExtraCroppingQuirk::class.java) == null)
-        }
+        assumeExtraCroppingQuirk(implName)
     }
 
     private class ImageSavedCallback :
@@ -1162,4 +1157,15 @@
     extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO) == "yes"
 
 internal fun MediaMetadataRetriever.getDuration(): Long? =
-    extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong()
\ No newline at end of file
+    extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong()
+
+@RequiresApi(21)
+fun assumeExtraCroppingQuirk(implName: String) {
+    val msg =
+        "Devices in ExtraCroppingQuirk will get a fixed resolution regardless of any settings"
+    if (implName.contains(CameraPipeConfig::class.simpleName!!)) {
+        assumeTrue(msg, PipeDeviceQuirks[PipeExtraCroppingQuirk::class.java] == null)
+    } else {
+        assumeTrue(msg, Camera2DeviceQuirks.get(Camera2ExtraCroppingQuirk::class.java) == null)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
index 4424a86..553e0a2 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import androidx.test.rule.GrantPermissionRule
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.Dispatchers
@@ -79,6 +80,11 @@
     }
 
     @get:Rule
+    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
+        android.Manifest.permission.RECORD_AUDIO
+    )
+
+    @get:Rule
     val cameraPipeConfigTestRule = CameraPipeConfigTestRule(
         active = implName == CameraPipeConfig::class.simpleName,
     )
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 c7a7b9b..8c83205 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
@@ -38,6 +38,7 @@
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 import static androidx.camera.core.impl.utils.Threads.isMainThread;
 import static androidx.camera.core.impl.utils.TransformUtils.rectToString;
+import static androidx.camera.core.impl.utils.TransformUtils.within360;
 import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_CLASS;
 import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME;
 import static androidx.camera.core.internal.ThreadConfig.OPTION_BACKGROUND_EXECUTOR;
@@ -247,13 +248,12 @@
      *
      * <p>The rotation can be set prior to constructing a VideoCapture using
      * {@link VideoCapture.Builder#setTargetRotation(int)} or dynamically by calling
-     * {@link VideoCapture#setTargetRotation(int)} or {@link #setTargetRotationDegrees(int)}.
+     * {@link VideoCapture#setTargetRotation(int)}.
      * If not set, the target rotation defaults to the value of {@link Display#getRotation()} of
      * the default display at the time the use case is bound.
      *
      * @return The rotation of the intended target.
      * @see VideoCapture#setTargetRotation(int)
-     * @see VideoCapture#setTargetRotationDegrees(int)
      */
     @RotationValue
     public int getTargetRotation() {
@@ -290,10 +290,11 @@
      * the target rotation. This way, the rotation output will indicate which way is down for a
      * given video. This is important since display orientation may be locked by device default,
      * user setting, or app configuration, and some devices may not transition to a
-     * reverse-portrait display orientation. In these cases, use
-     * {@link #setTargetRotationDegrees} to set target rotation dynamically according to the
-     * {@link android.view.OrientationEventListener}, without re-creating the use case.
-     * See {@link #setTargetRotationDegrees} for more information.
+     * reverse-portrait display orientation. In these cases, set target rotation dynamically
+     * according to the {@link android.view.OrientationEventListener}, without re-creating the
+     * use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to convert the
+     * orientation of the {@link android.view.OrientationEventListener} to a rotation value.
+     * See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code.
      *
      * <p>If not set, the target rotation will default to the value of
      * {@link Display#getRotation()} of the default display at the time the use case is bound. To
@@ -385,9 +386,12 @@
      * will choose a strategy according to the use case.
      *
      * @param degrees Desired rotation degree of the output video.
+     * @deprecated Use {@link UseCase#snapToSurfaceRotation(int)} and
+     * {@link #setTargetRotation(int)} to convert and set the rotation.
      */
+    @Deprecated // TODO(b/277999375): Remove API setTargetRotationDegrees.
     public void setTargetRotationDegrees(int degrees) {
-        setTargetRotation(orientationDegreesToSurfaceRotation(degrees));
+        setTargetRotation(snapToSurfaceRotation(within360(degrees)));
     }
 
     /**
@@ -741,7 +745,7 @@
      *
      */
     @Nullable
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     SurfaceEdge getCameraEdge() {
         return mCameraEdge;
     }
@@ -1479,7 +1483,7 @@
          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
          *
          * <p>In general, it is best to additionally set the target rotation dynamically on the
-         * use case. See {@link VideoCapture#setTargetRotationDegrees(int)} for additional
+         * use case. See {@link VideoCapture#setTargetRotation(int)} for additional
          * documentation.
          *
          * <p>If not set, the target rotation will default to the value of
@@ -1495,7 +1499,6 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          * @see VideoCapture#setTargetRotation(int)
-         * @see VideoCapture#setTargetRotationDegrees(int)
          * @see android.view.OrientationEventListener
          */
         @NonNull
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 9f31996..557a2a7 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
@@ -16,6 +16,9 @@
 
 package androidx.camera.video.internal.compat.quirk;
 
+import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
+
 import android.media.CamcorderProfile;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
@@ -23,12 +26,17 @@
 import android.os.Build;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
 /**
  * <p>QuirkSummary
- *     Bug Id: 192431846, 199582287, 218841498, 203481899, 216583006
+ *     Bug Id: 192431846, 199582287, 218841498, 203481899, 216583006, 278843124, 278855948
  *     Description: Quirk which denotes {@link MediaCodecInfo} queried by {@link MediaCodecList}
  *                  returns incorrect info.
  *                  On Nokia 1, {@link CamcorderProfile} indicates it can support resolutions
@@ -55,19 +63,18 @@
  *                  experimental result, H.264 + 3840x2160 can be used to record video on this
  *                  device. Hence use quirk to workaround this case. See b/203481899#comment2.
  *                  @link MediaCodecInfo} searched by {@link MediaCodecList#getCodecInfos()}
- *                  shows the maximum supported resolution of the AVC encoder is 1920x1072 on
- *                  Redmi note 4 and LG K10 LTE K430. However, the 1920x1080 option can be
- *                  successfully configured properly. See b/216583006.
- *
+ *                  shows the maximum supported resolution of the AVC encoder is 1920x1072.
+ *                  However, the 1920x1080 option can be successfully configured properly.
+ *                  See b/216583006, b/278843124, b/278855948.
  *     Device(s): Nokia 1, Motc C, X650, LG-X230, Positivo Twist 2 Pro, Huawei Mate9, Redmi note 4
- *                , LG K10 LTE K430
+ *                , LG K10 LTE K430, Samsung Galaxy A03 Core, Vivo Y75, Realme C11 2021
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class MediaCodecInfoReportIncorrectInfoQuirk implements Quirk {
 
     static boolean load() {
         return isNokia1() || isMotoC() || isX650() || isX230() || isHuaweiMate9()
-                || isPositivoTwist2Pro() || isRedmiNote4() || isLGK430();
+                || isPositivoTwist2Pro() || isFHDProblematicDevice();
     }
 
     private static boolean isNokia1() {
@@ -96,39 +103,62 @@
                 Build.MODEL);
     }
 
-    private static boolean isRedmiNote4() {
-        return "Xiaomi".equalsIgnoreCase(Build.BRAND) && "redmi note 4".equalsIgnoreCase(
-                Build.MODEL);
-    }
-
-    private static boolean isLGK430() {
-        return "lge".equalsIgnoreCase(Build.BRAND) && "lg-k430".equalsIgnoreCase(Build.MODEL);
-    }
+    public static final List<String> INCORRECT_FHD_PROFILE_MODEL_LIST = Arrays.asList(
+            "lg-k430",
+            "redmi note 4",
+            "rmx3231",
+            "v2117",
+            "sm-a032f",
+            "moto g(20)",
+            "sm-a035m"
+    );
 
     /** Check if problematic MediaFormat info for these candidate devices. */
     public boolean isUnSupportMediaCodecInfo(@NonNull MediaFormat mediaFormat) {
+        MediaFormatResolver formatResolver = new MediaFormatResolver(mediaFormat);
         if (isNokia1() || isMotoC() || isX650() || isX230() || isPositivoTwist2Pro()) {
-            /** Checks if the given mime type is a problematic mime type. */
-            String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
-            return MediaFormat.MIMETYPE_VIDEO_MPEG4.equals(mimeType);
-        } else if (isHuaweiMate9() && isVideoFormat(mediaFormat)) {
-            /** Checks if this is an unsupported resolution for avc. */
-            int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
-            int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
-            return (width == 3840 && height == 2160);
-        } else if (isRedmiNote4() || isLGK430()) {
-            if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(
-                    mediaFormat.getString(MediaFormat.KEY_MIME))) {
-                int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
-                int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
-                return width == 1920 && height == 1080;
-            }
+            return formatResolver.isMpeg4();
+        } else if (isHuaweiMate9()) {
+            return formatResolver.isVideo() && formatResolver.isSize(3840, 2160);
+        } else if (isFHDProblematicDevice()) {
+            return formatResolver.isAvc() && formatResolver.isSize(1920, 1080);
         }
         return false;
     }
 
-    private boolean isVideoFormat(@NonNull MediaFormat mediaFormat) {
-        String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
-        return mimeType.contains("video/");
+    private static boolean isFHDProblematicDevice() {
+        return INCORRECT_FHD_PROFILE_MODEL_LIST.contains(Build.MODEL.toLowerCase(Locale.US));
+    }
+
+    private static class MediaFormatResolver {
+        private final MediaFormat mMediaFormat;
+
+        MediaFormatResolver(@NonNull MediaFormat mediaFormat) {
+            mMediaFormat = mediaFormat;
+        }
+
+        boolean isVideo() {
+            String mimeType = getMime();
+            return mimeType != null && mimeType.contains("video/");
+        }
+
+        boolean isAvc() {
+            return MIMETYPE_VIDEO_AVC.equalsIgnoreCase(getMime());
+        }
+
+        boolean isMpeg4() {
+            return MIMETYPE_VIDEO_MPEG4.equalsIgnoreCase(getMime());
+        }
+
+        boolean isSize(int width, int height) {
+            int formatWidth = mMediaFormat.getInteger(MediaFormat.KEY_WIDTH);
+            int formatHeight = mMediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
+            return formatWidth == width && formatHeight == height;
+        }
+
+        @Nullable
+        private String getMime() {
+            return mMediaFormat.getString(MediaFormat.KEY_MIME);
+        }
     }
 }
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 7d1bb6d..93f2245 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
@@ -711,23 +711,6 @@
     }
 
     @Test
-    fun setTargetRotationDegrees() {
-        val videoCapture = createVideoCapture()
-        videoCapture.setTargetRotationDegrees(45)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_270)
-        videoCapture.setTargetRotationDegrees(135)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_180)
-        videoCapture.setTargetRotationDegrees(225)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_90)
-        videoCapture.setTargetRotationDegrees(315)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_0)
-        videoCapture.setTargetRotationDegrees(405)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_270)
-        videoCapture.setTargetRotationDegrees(-45)
-        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_0)
-    }
-
-    @Test
     fun hasSurfaceProcessingQuirk_nodeIsNeeded() {
         // Arrange.
         VideoCapture.sEnableSurfaceProcessingByQuirk = true
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
index 4a91e89..2f29672 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
@@ -16,8 +16,6 @@
 
 package androidx.camera.view;
 
-import static androidx.annotation.RestrictTo.Scope.TESTS;
-
 import android.Manifest;
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -28,7 +26,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraEffect;
@@ -154,7 +151,7 @@
 
     /**
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     @SuppressWarnings("FutureReturnValueIgnored")
     void shutDownForTests() {
         if (mCameraProvider != null) {
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
index 3247c70..b42e391 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
@@ -18,7 +18,7 @@
 
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraSelector;
@@ -68,6 +68,6 @@
      *
      */
     @NonNull
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     ListenableFuture<Void> shutdown();
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
index fb13889..30b38fc 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
@@ -20,6 +20,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraSelector;
@@ -66,6 +67,7 @@
         return mProcessCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
     }
 
+    @VisibleForTesting
     @NonNull
     @Override
     public ListenableFuture<Void> shutdown() {
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
index 42ed11b..056a07e 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
@@ -17,12 +17,9 @@
 package androidx.camera.integration.core
 
 import android.content.Context
-import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AE
 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AF
 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AWB
-import android.hardware.camera2.CaptureRequest
-import android.hardware.camera2.TotalCaptureResult
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.core.Camera
@@ -35,7 +32,6 @@
 import androidx.camera.core.FocusMeteringAction.FLAG_AWB
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.SurfaceOrientedMeteringPointFactory
-import androidx.camera.integration.core.util.CameraPipeUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
@@ -45,7 +41,6 @@
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
-import java.util.concurrent.CountDownLatch
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.Dispatchers
@@ -126,7 +121,6 @@
 
         ProcessCameraProvider.configureInstance(cameraXConfig)
         cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
-        val captureCallback = CameraSessionCaptureCallback()
 
         withContext(Dispatchers.Main) {
             val fakeLifecycleOwner = FakeLifecycleOwner()
@@ -134,23 +128,9 @@
             camera = cameraProvider.bindToLifecycle(
                 fakeLifecycleOwner,
                 cameraSelector,
-                ImageCapture.Builder().also { builder ->
-                    captureCallback.let {
-                        CameraPipeUtil.setCameraCaptureSessionCallback(
-                            implName,
-                            builder,
-                            it
-                        )
-                    }
-                }.build()
+                ImageCapture.Builder().build()
             )
         }
-
-        if (implName == CameraPipeConfig::class.simpleName) {
-            // TODO(b/263211462): Remove this waiting for camera opening to be completed
-            //  when focus metering request can be submitted without the waiting
-            captureCallback.await(5000)
-        }
     }
 
     @After
@@ -434,22 +414,4 @@
             assertThat(cause).isInstanceOf(CameraControl.OperationCanceledException::class.java)
         }
     }
-
-    class CameraSessionCaptureCallback : CameraCaptureSession.CaptureCallback() {
-        private val latch = CountDownLatch(1)
-
-        override fun onCaptureCompleted(
-            session: CameraCaptureSession,
-            request: CaptureRequest,
-            result: TotalCaptureResult
-        ) {
-            latch.countDown()
-        }
-
-        suspend fun await(timeoutMs: Long = 10000) {
-            withContext(Dispatchers.IO) {
-                latch.await(timeoutMs, TimeUnit.MILLISECONDS)
-            }
-        }
-    }
 }
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
index 429f3c8..2b39b56 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
@@ -42,6 +42,7 @@
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.LabTestRule
 import androidx.camera.testing.SurfaceTextureProvider
+import androidx.concurrent.futures.await
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executors
 import kotlinx.coroutines.CompletableDeferred
@@ -100,15 +101,14 @@
         val executorForGL = Executors.newSingleThreadExecutor()
         // Some OEM requires frames drain (updateTexImage being invoked) in SurfaceTexture,
         // otherwise it might cause still capture to fail.
-        val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTexture(
+        val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
             executorForGL,
             previewSize.width,
-            previewSize.height
-        ) {
-            if (!deferredPreviewFrame.isCompleted) {
-                deferredPreviewFrame.complete(it)
-            }
-        }
+            previewSize.height, {
+                if (!deferredPreviewFrame.isCompleted) {
+                    deferredPreviewFrame.complete(it)
+                }
+            }) { executorForGL.shutdown() }.await()
         val previewSurface = Surface(surfaceTextureHolder.surfaceTexture)
 
         // Still capture surface
@@ -182,7 +182,6 @@
         previewSurface.release()
         captureSurface.release()
         surfaceTextureHolder.close()
-        executorForGL.shutdown()
     }
 
     /**
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 81ea34c..610e9ae 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
@@ -136,6 +136,40 @@
     }
 
     @Test
+    fun enableEffect_previewEffectIsEnabled() {
+        // Arrange: launch app and verify effect is inactive.
+        fragment.assertPreviewIsStreaming()
+        val processor =
+            fragment.mToneMappingSurfaceEffect.surfaceProcessor as ToneMappingSurfaceProcessor
+        assertThat(processor.isSurfaceRequestedAndProvided()).isFalse()
+
+        // Act: turn on effect.
+        val effectToggleId = "androidx.camera.integration.view:id/effect_toggle"
+        uiDevice.findObject(UiSelector().resourceId(effectToggleId)).click()
+        instrumentation.waitForIdleSync()
+
+        // Assert: verify that effect is active.
+        assertThat(processor.isSurfaceRequestedAndProvided()).isTrue()
+    }
+
+    @Test
+    fun enableEffect_imageCaptureEffectIsEnabled() {
+        // Arrange: launch app and verify effect is inactive.
+        fragment.assertPreviewIsStreaming()
+        val effect = fragment.mToneMappingImageEffect as ToneMappingImageEffect
+        assertThat(effect.isInvoked()).isFalse()
+
+        // Act: turn on effect.
+        val effectToggleId = "androidx.camera.integration.view:id/effect_toggle"
+        uiDevice.findObject(UiSelector().resourceId(effectToggleId)).click()
+        instrumentation.waitForIdleSync()
+        fragment.assertCanTakePicture()
+
+        // Assert: verify that effect is active.
+        assertThat(effect.isInvoked()).isTrue()
+    }
+
+    @Test
     fun controllerBound_canGetCameraControl() {
         fragment.assertPreviewIsStreaming()
         instrumentation.runOnMainSync {
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/EffectsFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/EffectsFragmentTest.kt
deleted file mode 100644
index 85c2ca4..0000000
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/EffectsFragmentTest.kt
+++ /dev/null
@@ -1,190 +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.camera.integration.view
-
-import android.net.Uri
-import android.os.Build
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.camera2.pipe.integration.CameraPipeConfig
-import androidx.camera.core.CameraXConfig
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.ImageCaptureException
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.camera.testing.CameraPipeConfigTestRule
-import androidx.camera.testing.CameraUtil
-import androidx.camera.testing.CoreAppTestUtil
-import androidx.camera.view.PreviewView
-import androidx.fragment.app.testing.FragmentScenario
-import androidx.lifecycle.Lifecycle
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.LargeTest
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.rule.GrantPermissionRule
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Semaphore
-import java.util.concurrent.TimeUnit
-import org.junit.After
-import org.junit.Assume.assumeFalse
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * Instrument tests for {@link EffectsFragment}.
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class EffectsFragmentTest(
-    private val implName: String,
-    private val cameraConfig: CameraXConfig
-) {
-    @get:Rule
-    val cameraPipeConfigTestRule = CameraPipeConfigTestRule(
-        active = implName == CameraPipeConfig::class.simpleName,
-    )
-
-    @get:Rule
-    val useCameraRule = CameraUtil.grantCameraPermissionAndPreTest(
-        CameraControllerFragmentTest.testCameraRule,
-        CameraUtil.PreTestCameraIdList(cameraConfig)
-    )
-
-    @get:Rule
-    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
-        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
-        android.Manifest.permission.RECORD_AUDIO
-    )
-
-    private val instrumentation = InstrumentationRegistry.getInstrumentation()
-    private lateinit var cameraProvider: ProcessCameraProvider
-    private lateinit var fragment: EffectsFragment
-    private lateinit var fragmentScenario: FragmentScenario<EffectsFragment>
-
-    @Before
-    fun setup() {
-        // 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)
-        ProcessCameraProvider.configureInstance(cameraConfig)
-        cameraProvider = ProcessCameraProvider.getInstance(
-            ApplicationProvider.getApplicationContext()
-        )[10000, TimeUnit.MILLISECONDS]
-        fragmentScenario = FragmentScenario.launchInContainer(
-            EffectsFragment::class.java, null, R.style.AppTheme,
-            null
-        )
-        fragment = fragmentScenario.getFragment()
-    }
-
-    @After
-    fun tearDown() {
-        if (::fragmentScenario.isInitialized) {
-            fragmentScenario.moveToState(Lifecycle.State.DESTROYED)
-        }
-
-        if (::cameraProvider.isInitialized) {
-            cameraProvider.shutdown()[10000, TimeUnit.MILLISECONDS]
-        }
-    }
-
-    @Test
-    fun launchFragment_surfaceProcessorIsActive() {
-        // Arrange.
-        fragment.assertPreviewStreaming()
-        // Assert.
-        assertThat(fragment.getSurfaceProcessor().isSurfaceRequestedAndProvided()).isTrue()
-    }
-
-    @Test
-    fun takePicture_imageEffectInvoked() {
-        // Arrange.
-        fragment.assertPreviewStreaming()
-        // Act.
-        fragment.assertCanTakePicture()
-        // Assert.
-        assertThat(fragment.getImageEffect()!!.isInvoked()).isTrue()
-    }
-
-    @Test
-    fun shareToImageCapture_canTakePicture() {
-        assumeFalse(
-            "Cuttlefish 31+ cannot take pictures. Unable to test.",
-            Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT >= 31
-        )
-        // Act.
-        instrumentation.runOnMainSync {
-            fragment.surfaceEffectForImageCapture.isChecked = true
-        }
-        // Assert.
-        fragment.assertPreviewStreaming()
-        fragment.assertCanTakePicture()
-        assertThat(fragment.getImageEffect()).isNull()
-    }
-
-    private fun FragmentScenario<EffectsFragment>.getFragment(): EffectsFragment {
-        var fragment: EffectsFragment? = null
-        this.onFragment { newValue: EffectsFragment -> fragment = newValue }
-        return fragment!!
-    }
-
-    private fun EffectsFragment.assertCanTakePicture() {
-        val imageCallbackSemaphore = Semaphore(0)
-        var uri: Uri? = null
-        instrumentation.runOnMainSync {
-            this.takePicture(object : ImageCapture.OnImageSavedCallback {
-                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
-                    uri = outputFileResults.savedUri
-                    imageCallbackSemaphore.release()
-                }
-
-                override fun onError(exception: ImageCaptureException) {
-                    throw exception
-                }
-            })
-        }
-        assertThat(imageCallbackSemaphore.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue()
-        assertThat(uri).isNotNull()
-    }
-
-    private fun EffectsFragment.assertPreviewStreaming() {
-        val previewStreaming = Semaphore(0)
-        instrumentation.runOnMainSync {
-            previewView.previewStreamState.observe(
-                this
-            ) {
-                if (it == PreviewView.StreamState.STREAMING) {
-                    previewStreaming.release()
-                }
-            }
-        }
-        assertThat(previewStreaming.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue()
-    }
-
-    companion object {
-
-        const val TIMEOUT_SECONDS = 10L
-
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun data() = listOf(
-            arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
-            arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
-        )
-    }
-}
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index 26550d3..1d771cc 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -20,6 +20,8 @@
 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
 import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_NONE;
 
+import static java.util.Arrays.asList;
+
 import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.Dialog;
@@ -56,7 +58,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.ImageAnalysis;
@@ -86,6 +87,7 @@
 import java.nio.ByteBuffer;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.concurrent.ExecutorService;
@@ -108,6 +110,7 @@
     private FrameLayout mContainer;
     private Button mFlashMode;
     private ToggleButton mCameraToggle;
+    private ToggleButton mEffectToggle;
     private ExecutorService mExecutorService;
     private ToggleButton mCaptureEnabledToggle;
     private ToggleButton mAnalysisEnabledToggle;
@@ -145,6 +148,10 @@
     @Nullable
     private ImageAnalysis.Analyzer mWrappedAnalyzer;
 
+    @VisibleForTesting
+    ToneMappingSurfaceEffect mToneMappingSurfaceEffect;
+    ToneMappingImageEffect mToneMappingImageEffect;
+
     private final ImageAnalysis.Analyzer mAnalyzer = image -> {
         byte[] bytes = new byte[image.getPlanes()[0].getBuffer().remaining()];
         image.getPlanes()[0].getBuffer().get(bytes);
@@ -213,6 +220,13 @@
             }
         });
 
+        // Set up post-processing effects.
+        mToneMappingSurfaceEffect = new ToneMappingSurfaceEffect();
+        mToneMappingImageEffect = new ToneMappingImageEffect();
+        mEffectToggle = view.findViewById(R.id.effect_toggle);
+        mEffectToggle.setOnCheckedChangeListener((compoundButton, isChecked) -> onEffectsToggled());
+        onEffectsToggled();
+
         // Set up the button to change the PreviewView's size.
         view.findViewById(R.id.shrink).setOnClickListener(v -> {
             // Shrinks PreviewView by 10% each time it's clicked.
@@ -355,6 +369,16 @@
             mExecutorService.shutdown();
         }
         mRotationProvider.removeListener(mRotationListener);
+        mToneMappingSurfaceEffect.release();
+    }
+
+    private void onEffectsToggled() {
+        if (mEffectToggle.isChecked()) {
+            mCameraController.setEffects(
+                    new HashSet<>(asList(mToneMappingSurfaceEffect, mToneMappingImageEffect)));
+        } else {
+            mCameraController.clearEffects();
+        }
     }
 
     void checkFailedFuture(ListenableFuture<Void> voidFuture) {
@@ -585,22 +609,22 @@
     // For testing
     // -----------------
 
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     LifecycleCameraController getCameraController() {
         return mCameraController;
     }
 
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     void setWrappedAnalyzer(@Nullable ImageAnalysis.Analyzer analyzer) {
         mWrappedAnalyzer = analyzer;
     }
 
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     PreviewView getPreviewView() {
         return mPreviewView;
     }
 
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     int getSensorRotation() {
         return mRotation;
     }
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/EffectsFragment.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/EffectsFragment.kt
deleted file mode 100644
index 4f5dbd9..0000000
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/EffectsFragment.kt
+++ /dev/null
@@ -1,251 +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.camera.integration.view
-
-import android.annotation.SuppressLint
-import android.content.ContentValues
-import android.os.Bundle
-import android.os.Environment
-import android.provider.MediaStore
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Button
-import android.widget.RadioButton
-import android.widget.RadioGroup
-import android.widget.Toast
-import androidx.annotation.OptIn
-import androidx.annotation.VisibleForTesting
-import androidx.camera.core.CameraEffect
-import androidx.camera.core.CameraEffect.IMAGE_CAPTURE
-import androidx.camera.core.CameraEffect.PREVIEW
-import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.ImageCapture.OutputFileOptions
-import androidx.camera.core.ImageCaptureException
-import androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor
-import androidx.camera.video.MediaStoreOutputOptions
-import androidx.camera.video.Recording
-import androidx.camera.video.VideoRecordEvent
-import androidx.camera.view.CameraController
-import androidx.camera.view.LifecycleCameraController
-import androidx.camera.view.PreviewView
-import androidx.camera.view.video.AudioConfig
-import androidx.camera.view.video.ExperimentalVideo
-import androidx.fragment.app.Fragment
-
-/**
- * Fragment for testing effects integration.
- */
-@OptIn(markerClass = [ExperimentalVideo::class])
-class EffectsFragment : Fragment() {
-
-    private lateinit var cameraController: LifecycleCameraController
-    lateinit var previewView: PreviewView
-    private lateinit var surfaceEffectForPreviewVideo: RadioButton
-    lateinit var surfaceEffectForImageCapture: RadioButton
-    private lateinit var imageEffectForImageCapture: RadioButton
-    private lateinit var previewVideoGroup: RadioGroup
-    private lateinit var imageGroup: RadioGroup
-    private lateinit var capture: Button
-    private lateinit var record: Button
-    private var recording: Recording? = null
-
-    private lateinit var surfaceProcessor: ToneMappingSurfaceProcessor
-    private var imageEffect: ToneMappingImageEffect? = null
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
-        // Inflate the layout for this fragment.
-        val view = inflater.inflate(R.layout.effects_view, container, false)
-        previewView = view.findViewById(R.id.preview_view)
-        surfaceEffectForPreviewVideo = view.findViewById(R.id.surface_effect_for_preview_video)
-        surfaceEffectForImageCapture = view.findViewById(R.id.surface_effect_for_image_capture)
-        imageEffectForImageCapture = view.findViewById(R.id.image_effect_for_image_capture)
-        previewVideoGroup = view.findViewById(R.id.preview_and_video_effect_group)
-        imageGroup = view.findViewById(R.id.image_effect_group)
-        capture = view.findViewById(R.id.capture)
-        record = view.findViewById(R.id.record)
-
-        // Set up  UI events.
-        // previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
-        previewVideoGroup.setOnCheckedChangeListener { _, _ -> updateEffects() }
-        imageGroup.setOnCheckedChangeListener { _, _ -> updateEffects() }
-        capture.setOnClickListener { takePicture() }
-        record.setOnClickListener {
-            if (recording == null) {
-                startRecording()
-            } else {
-                stopRecording()
-            }
-        }
-
-        // Set up the surface processor.
-        surfaceProcessor = ToneMappingSurfaceProcessor()
-
-        // Set up the camera controller.
-        cameraController = LifecycleCameraController(requireContext())
-        cameraController.setEnabledUseCases(
-            CameraController.IMAGE_CAPTURE or CameraController.VIDEO_CAPTURE
-        )
-        previewView.controller = cameraController
-        updateEffects()
-        cameraController.bindToLifecycle(viewLifecycleOwner)
-
-        return view
-    }
-
-    private fun updateEffects() {
-        val effects = mutableSetOf<CameraEffect>()
-        if (surfaceEffectForPreviewVideo.isChecked && surfaceEffectForImageCapture.isChecked) {
-            // Sharing surface effect to all 3 use cases
-            effects.add(
-                ToneMappingSurfaceEffect(
-                    PREVIEW or IMAGE_CAPTURE or VIDEO_CAPTURE,
-                    surfaceProcessor
-                )
-            )
-        } else if (surfaceEffectForPreviewVideo.isChecked) {
-            // Sharing surface effect to preview and video
-            effects.add(
-                ToneMappingSurfaceEffect(
-                    PREVIEW or VIDEO_CAPTURE,
-                    surfaceProcessor
-                )
-            )
-        } else if (
-            !surfaceEffectForPreviewVideo.isChecked && surfaceEffectForImageCapture.isChecked) {
-            toast(
-                "Cannot apply SurfaceProcessor to ImageCapture " +
-                    "without applying it to Preview and VideoCapture."
-            )
-        }
-
-        if (imageEffectForImageCapture.isChecked) {
-            // Use ImageEffect for image capture
-            imageEffect = ToneMappingImageEffect()
-            effects.add(imageEffect!!)
-        } else {
-            imageEffect = null
-        }
-        cameraController.setEffects(effects)
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        surfaceProcessor.release()
-    }
-
-    private fun toast(message: String?) {
-        requireActivity().runOnUiThread {
-            Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
-        }
-    }
-
-    private fun takePicture() {
-        takePicture(
-            object : ImageCapture.OnImageSavedCallback {
-                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
-                    toast("Image saved successfully.")
-                }
-
-                override fun onError(exception: ImageCaptureException) {
-                    toast("Image capture failed. $exception")
-                }
-            }
-        )
-    }
-
-    fun takePicture(onImageSavedCallback: ImageCapture.OnImageSavedCallback) {
-        createDefaultPictureFolderIfNotExist()
-        val contentValues = ContentValues()
-        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
-        val outputFileOptions = OutputFileOptions.Builder(
-            requireContext().contentResolver,
-            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-            contentValues
-        ).build()
-        cameraController.takePicture(
-            outputFileOptions,
-            directExecutor(),
-            onImageSavedCallback
-        )
-    }
-
-    @SuppressLint("MissingPermission")
-    fun startRecording() {
-        record.text = "Stop recording"
-        val outputOptions: MediaStoreOutputOptions = getNewVideoOutputMediaStoreOptions()
-        val audioConfig = AudioConfig.create(true)
-        recording = cameraController.startRecording(
-            outputOptions, audioConfig,
-            directExecutor()
-        ) {
-            if (it is VideoRecordEvent.Finalize) {
-                val uri = it.outputResults.outputUri
-                if (it.error == VideoRecordEvent.Finalize.ERROR_NONE) {
-                    toast("Video saved to: $uri")
-                } else {
-                    toast("Failed to save video: uri $uri with code (${it.error})")
-                }
-            }
-        }
-    }
-
-    private fun stopRecording() {
-        record.text = "Record"
-        recording?.stop()
-    }
-
-    private fun getNewVideoOutputMediaStoreOptions(): MediaStoreOutputOptions {
-        val videoFileName = "video_" + System.currentTimeMillis()
-        val resolver = requireContext().contentResolver
-        val contentValues = ContentValues()
-        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
-        contentValues.put(MediaStore.Video.Media.TITLE, videoFileName)
-        contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName)
-        return MediaStoreOutputOptions.Builder(
-            resolver,
-            MediaStore.Video.Media.EXTERNAL_CONTENT_URI
-        ).setContentValues(contentValues)
-            .build()
-    }
-
-    private fun createDefaultPictureFolderIfNotExist() {
-        val pictureFolder = Environment.getExternalStoragePublicDirectory(
-            Environment.DIRECTORY_PICTURES
-        )
-        if (!pictureFolder.exists()) {
-            if (!pictureFolder.mkdir()) {
-                toast("Failed to create directory: $pictureFolder")
-            }
-        }
-    }
-
-    @VisibleForTesting
-    fun getImageEffect(): ToneMappingImageEffect? {
-        return imageEffect
-    }
-
-    @VisibleForTesting
-    fun getSurfaceProcessor(): ToneMappingSurfaceProcessor {
-        return surfaceProcessor
-    }
-}
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
index 6e40f89..970b5d8 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
@@ -190,9 +190,6 @@
             case R.id.mlkit:
                 mFragmentType = FragmentType.MLKIT;
                 break;
-            case R.id.effects:
-                mFragmentType = FragmentType.EFFECTS;
-                break;
         }
         startFragment();
         return true;
@@ -225,9 +222,6 @@
             case MLKIT:
                 startFragment(R.string.mlkit, new MlKitFragment());
                 break;
-            case EFFECTS:
-                startFragment(R.string.effects, new EffectsFragment());
-                break;
         }
     }
 
@@ -249,6 +243,6 @@
     }
 
     private enum FragmentType {
-        PREVIEW_VIEW, CAMERA_CONTROLLER, TRANSFORM, COMPOSE_UI, MLKIT, EFFECTS
+        PREVIEW_VIEW, CAMERA_CONTROLLER, TRANSFORM, COMPOSE_UI, MLKIT
     }
 }
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingImageEffect.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingImageEffect.kt
index 6cb06f0..8f35484 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingImageEffect.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingImageEffect.kt
@@ -35,15 +35,15 @@
 ) {
 
     fun isInvoked(): Boolean {
-        return (imageProcessor as ToneMappingImageProcessor).processed
+        return (imageProcessor as ToneMappingImageProcessor).processoed
     }
 
     private class ToneMappingImageProcessor : ImageProcessor {
 
-        var processed = false
+        var processoed = false
 
         override fun process(request: ImageProcessor.Request): Response {
-            processed = true
+            processoed = true
             val inputImage = request.inputImage as RgbaImageProxy
             val bitmap = inputImage.createBitmap()
             applyToneMapping(bitmap)
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
index 524a65f..3d18aff 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
@@ -22,15 +22,11 @@
 /**
  * A tone mapping effect for Preview/VideoCapture UseCase.
  */
-internal class ToneMappingSurfaceEffect(
-    targets: Int = PREVIEW or VIDEO_CAPTURE,
-    private val processor: ToneMappingSurfaceProcessor = ToneMappingSurfaceProcessor()
-) :
-    CameraEffect(
-        targets, mainThreadExecutor(), processor
-    ) {
+internal class ToneMappingSurfaceEffect : CameraEffect(
+    PREVIEW or VIDEO_CAPTURE, mainThreadExecutor(), ToneMappingSurfaceProcessor()
+) {
 
     fun release() {
-        (processor as? ToneMappingSurfaceProcessor)?.release()
+        (surfaceProcessor as? ToneMappingSurfaceProcessor)?.release()
     }
 }
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout-land/camera_controller_view.xml b/camera/integration-tests/viewtestapp/src/main/res/layout-land/camera_controller_view.xml
index f8cd281..127eedd 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/layout-land/camera_controller_view.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/layout-land/camera_controller_view.xml
@@ -51,6 +51,12 @@
                 android:layout_height="wrap_content"
                 android:textOff="@string/toggle_camera_front"
                 android:textOn="@string/toggle_camera_back" />
+            <ToggleButton
+                android:id="@+id/effect_toggle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textOff="@string/toggle_effect_off"
+                android:textOn="@string/toggle_effect_on" />
         </LinearLayout>
 
         <LinearLayout
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout-land/effects_view.xml b/camera/integration-tests/viewtestapp/src/main/res/layout-land/effects_view.xml
deleted file mode 100644
index e854580..0000000
--- a/camera/integration-tests/viewtestapp/src/main/res/layout-land/effects_view.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  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.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <RelativeLayout
-        android:layout_width="0dp"
-        android:layout_weight="1"
-        android:layout_height="match_parent">
-        <androidx.camera.view.PreviewView
-            android:id="@+id/preview_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:gravity="center_horizontal">
-            <Button
-                android:id="@+id/capture"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/btn_capture"
-                android:layout_margin="15dp"/>
-            <Button
-                android:id="@+id/record"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/btn_video_record"
-                android:layout_margin="15dp"/>
-        </LinearLayout>
-    </RelativeLayout>
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_margin="20dp">
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textStyle="bold"
-            android:text="@string/preview_and_video"/>
-        <RadioGroup
-            android:id="@+id/preview_and_video_effect_group"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content">
-            <RadioButton
-                android:id="@+id/surface_effect_for_preview_video"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:checked="true"
-                android:text="@string/surface_effect"/>
-            <RadioButton
-                android:id="@+id/no_effect_for_preview_video"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:checked="false"
-                android:text="@string/no_effect"/>
-        </RadioGroup>
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textStyle="bold"
-            android:layout_marginTop="20dp"
-            android:text="@string/image_capture"/>
-        <RadioGroup
-            android:id="@+id/image_effect_group"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content">
-            <RadioButton
-                android:id="@+id/surface_effect_for_image_capture"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:checked="false"
-                android:text="@string/surface_effect"/>
-            <RadioButton
-                android:id="@+id/image_effect_for_image_capture"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:checked="true"
-                android:text="@string/image_effect"/>
-            <RadioButton
-                android:id="@+id/no_effect_for_image_capture"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:checked="false"
-                android:text="@string/no_effect"/>
-        </RadioGroup>
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml b/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
index d605440..abdbd23 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
@@ -48,6 +48,12 @@
             android:layout_height="wrap_content"
             android:textOff="@string/toggle_camera_front"
             android:textOn="@string/toggle_camera_back" />
+        <ToggleButton
+            android:id="@+id/effect_toggle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textOff="@string/toggle_effect_off"
+            android:textOn="@string/toggle_effect_on" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout/effects_view.xml b/camera/integration-tests/viewtestapp/src/main/res/layout/effects_view.xml
deleted file mode 100644
index bb902eb..0000000
--- a/camera/integration-tests/viewtestapp/src/main/res/layout/effects_view.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  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.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_weight="1"
-        android:layout_height="0dp">
-        <androidx.camera.view.PreviewView
-            android:id="@+id/preview_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:gravity="center_horizontal">
-            <Button
-                android:id="@+id/capture"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/btn_capture"
-                android:layout_margin="15dp"/>
-            <Button
-                android:id="@+id/record"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/btn_video_record"
-                android:layout_margin="15dp"/>
-        </LinearLayout>
-    </RelativeLayout>
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_margin="20dp">
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent">
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textStyle="bold"
-                android:text="@string/preview_and_video"/>
-            <RadioGroup
-                android:id="@+id/preview_and_video_effect_group"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content">
-                <RadioButton
-                    android:id="@+id/surface_effect_for_preview_video"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:checked="true"
-                    android:text="@string/surface_effect"/>
-                <RadioButton
-                    android:id="@+id/no_effect_for_preview_video"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:checked="false"
-                    android:text="@string/no_effect"/>
-            </RadioGroup>
-        </LinearLayout>
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent">
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textStyle="bold"
-                android:text="@string/image_capture"/>
-            <RadioGroup
-                android:id="@+id/image_effect_group"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content">
-                <RadioButton
-                    android:id="@+id/surface_effect_for_image_capture"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:checked="false"
-                    android:text="@string/surface_effect"/>
-                <RadioButton
-                    android:id="@+id/image_effect_for_image_capture"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:checked="true"
-                    android:text="@string/image_effect"/>
-                <RadioButton
-                    android:id="@+id/no_effect_for_image_capture"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:checked="false"
-                    android:text="@string/no_effect"/>
-            </RadioGroup>
-        </LinearLayout>
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
index d736286..4a8d889 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
@@ -36,8 +36,4 @@
         android:id="@+id/mlkit"
         android:title="@string/mlkit"
         app:showAsAction="never" />
-    <item
-        android:id="@+id/effects"
-        android:title="@string/effects"
-        app:showAsAction="never" />
 </menu>
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml b/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
index d70d3f8..1c62930 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
@@ -16,12 +16,7 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
 
     <string name="app_name">CameraX Views Demo</string>
-    <string name="preview_and_video">Preview &amp; Video</string>
-    <string name="effects">Effects</string>
-    <string name="surface_effect">Surface effect</string>
-    <string name="no_effect">No effect</string>
-    <string name="image_effect">Image effect</string>
-    <string name="image_capture">ImageCapture</string>
+
     <string name="preview">Preview</string>
     <string name="btn_capture">Capture</string>
     <string name="btn_video_record">Record</string>
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl
new file mode 100644
index 0000000..26e7e22
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface ICarAppActivity {
+  oneway void setSurfacePackage(in androidx.car.app.serialization.Bundleable surfacePackage) = 1;
+  oneway void setSurfaceListener(androidx.car.app.activity.renderer.surface.ISurfaceListener listener) = 2;
+  oneway void registerRendererCallback(androidx.car.app.activity.renderer.IRendererCallback callback) = 3;
+  oneway void onStartInput() = 4;
+  oneway void onStopInput() = 5;
+  oneway void startCarApp(in android.content.Intent intent) = 6;
+  oneway void finishCarApp() = 7;
+  oneway void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd) = 8;
+  oneway void setInsetsListener(androidx.car.app.activity.renderer.IInsetsListener listener) = 9;
+  oneway void showAssist(in android.os.Bundle args) = 10;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl
new file mode 100644
index 0000000..1f3159d
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IInsetsListener {
+  /**
+   * @deprecated Use onWindowInsetsChanged(Insets, Insets) instead.
+   */
+  void onInsetsChanged(in android.graphics.Insets insets) = 1;
+  void onWindowInsetsChanged(in android.graphics.Insets insets, in android.graphics.Insets safeInsets) = 2;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
new file mode 100644
index 0000000..a0e60d4
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IProxyInputConnection {
+  CharSequence getTextBeforeCursor(int length, int flags) = 1;
+  CharSequence getTextAfterCursor(int length, int flags) = 2;
+  CharSequence getSelectedText(int flags) = 3;
+  int getCursorCapsMode(int reqModes) = 4;
+  boolean deleteSurroundingText(int beforeLength, int afterLength) = 5;
+  boolean setComposingText(CharSequence text, int newCursorPosition) = 6;
+  boolean setComposingRegion(int start, int end) = 7;
+  boolean finishComposingText() = 8;
+  boolean commitText(CharSequence text, int newCursorPosition) = 9;
+  boolean setSelection(int start, int end) = 10;
+  boolean performEditorAction(int editorAction) = 11;
+  boolean performContextMenuAction(int id) = 12;
+  boolean beginBatchEdit() = 13;
+  boolean endBatchEdit() = 14;
+  boolean sendKeyEvent(in android.view.KeyEvent event) = 15;
+  boolean clearMetaKeyStates(int states) = 16;
+  boolean reportFullscreenMode(boolean enabled) = 17;
+  boolean performPrivateCommand(String action, in android.os.Bundle data) = 18;
+  boolean requestCursorUpdates(int cursorUpdateMode) = 19;
+  boolean commitCorrection(in android.view.inputmethod.CorrectionInfo correctionInfo) = 20;
+  boolean commitCompletion(in android.view.inputmethod.CompletionInfo text) = 21;
+  android.view.inputmethod.ExtractedText getExtractedText(in android.view.inputmethod.ExtractedTextRequest request, int flags) = 22;
+  void closeConnection() = 23;
+  android.view.inputmethod.EditorInfo getEditorInfo() = 24;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl
new file mode 100644
index 0000000..8233bd1
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IRendererCallback {
+  void onBackPressed() = 1;
+  void onCreate() = 2;
+  void onStart() = 3;
+  void onResume() = 4;
+  void onPause() = 5;
+  void onStop() = 6;
+  void onDestroyed() = 7;
+  androidx.car.app.activity.renderer.IProxyInputConnection onCreateInputConnection(in android.view.inputmethod.EditorInfo editorInfo) = 8;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl
new file mode 100644
index 0000000..f2b84b8
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IRendererService {
+  boolean initialize(androidx.car.app.activity.renderer.ICarAppActivity carActivity, in android.content.ComponentName serviceName, int displayId) = 1;
+  boolean onNewIntent(in android.content.Intent intent, in android.content.ComponentName serviceName, int displayId) = 2;
+  void terminate(in android.content.ComponentName serviceName) = 3;
+  androidx.car.app.serialization.Bundleable performHandshake(in android.content.ComponentName serviceName, int appLatestApiLevel) = 4;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl
new file mode 100644
index 0000000..700162d
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer.surface;
+/* @hide */
+interface ISurfaceControl {
+  oneway void setSurfaceWrapper(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 1;
+  oneway void onTouchEvent(in android.view.MotionEvent event) = 2;
+  oneway void onWindowFocusChanged(boolean hasFocus, boolean isInTouchMode) = 3;
+  oneway void onKeyEvent(in android.view.KeyEvent event) = 4;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl
new file mode 100644
index 0000000..582319c1
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer.surface;
+/* @hide */
+interface ISurfaceListener {
+  oneway void onSurfaceAvailable(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 1;
+  oneway void onSurfaceChanged(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 2;
+  oneway void onSurfaceDestroyed(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 3;
+}
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl
deleted file mode 100644
index f1529b1..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable ComponentName;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl
deleted file mode 100644
index 0c8c241..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable Intent;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl
deleted file mode 100644
index cfaff66..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.view;
-
-@JavaOnlyStableParcelable parcelable KeyEvent;
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
index fb180a2..e724acf 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
@@ -125,12 +125,12 @@
     <string name="package_not_found_error_msg" msgid="7525619456883627939">"ጥቅል አልተገኘም።"</string>
     <string name="permissions_granted_msg" msgid="2348556088141992714">"ሁሉም ፈቃዶች ተሰጥተዋል። እባክዎን ከቅንብሮች ፈቃዶችን ይሻሩ።"</string>
     <string name="needs_access_msg_prefix" msgid="2204136858798832382">"መተግበሪያው ለሚከተሉት ፈቃዶች መዳረሻ ያስፈልገዋል፦\n"</string>
-    <string name="phone_screen_permission_msg" msgid="3599815596923367256">"በስልኩ ማያ ገጽ ላይ ፈቃድ ይስጡ"</string>
+    <string name="phone_screen_permission_msg" msgid="3599815596923367256">"በስልኩ ማያ ገፅ ላይ ፈቃድ ይስጡ"</string>
     <string name="enable_location_permission_on_device_msg" msgid="472752487966156897">"በመሣሪያ ላይ የአካባቢ ፈቃዶችን አንቃ"</string>
-    <string name="enable_location_permission_on_phone_msg" msgid="5082615523959139121">"በስልኩ ማያ ገጽ ላይ አካባቢን አንቃ"</string>
+    <string name="enable_location_permission_on_phone_msg" msgid="5082615523959139121">"በስልኩ ማያ ገፅ ላይ አካባቢን አንቃ"</string>
     <string name="required_permissions_title" msgid="5351791879153568211">"የሚያስፈልጉ ፈቃዶች"</string>
     <string name="request_permissions_title" msgid="7456426341142412300">"የፈቃድ ቅንጭብ ማሳያን ጠይቅ"</string>
-    <string name="cancel_reservation_title" msgid="1374986823057959608">"የቦታ ማስያዣ ማይ ገጽ ይቅር"</string>
+    <string name="cancel_reservation_title" msgid="1374986823057959608">"የቦታ ማስያዣ ማይ ገፅ ይቅር"</string>
     <string name="reservation_cancelled_msg" msgid="6334213670275547875">"ቦታ ማስያዣ ተሰርዟል"</string>
     <string name="result_demo_title" msgid="3900525190662148290">"የውጤት ቅንጭብ ማሳያ"</string>
     <string name="not_started_for_result_msg" msgid="7498800528148447270">"ይህ መተግበሪያ ለውጤት አልተጀመረም"</string>
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"አንዳንድ ተጨማሪ ጽሁፍ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"የናሙና ሊመረጥ የሚችል ዝርዝር"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"የተግባር ገደብ ቅንጭብ ማሳያ"</string>
-    <string name="task_limit_reached_msg" msgid="7162842196382260992">"ይህ የእርምጃ ቁጥር ፍሰቱ ከመጠን በላይ እንዲሆን ያደርገዋል እና ወደ አዲስ ማያ ገጽ ይመራዎታል።"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"ይህ የእርምጃ ቁጥር ፍሰቱ ከመጠን በላይ እንዲሆን ያደርገዋል እና ወደ አዲስ ማያ ገፅ ይመራዎታል።"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"የተግባር እርምጃ %1$d ከ%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ወደፊት ለመሄድ ጠቅ ያድርጉ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"እባክዎ የተለያዩ የቅንብር ደንቦችን ይጎብኙ እና መኪናው የማሽከርከር ሁነታ ላይ እንደሆነ ያረጋግጡ"</string>
@@ -333,7 +333,7 @@
     <string name="showcase_demos_title" msgid="1542092687878113304">"የመሳያ ቅንጭብ ማሳያ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"የቅንብር ደንብ አቀማመጥ ቅንጭብ ማሳያዎች"</string>
     <string name="grid_template_menu_demo_title" msgid="7096285873490705119">"የፍርግርግ ቅንብር ደንብ ቅንጭብ ማሳያዎች"</string>
-    <string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገጽ"</string>
+    <string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገፅ"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"የተጠቃሚ መስተጋብሮች"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"የቅንጭብ ማሳያ ፈቃዶችን ይጠይቁ"</string>
     <string name="application_overflow_title" msgid="396427940886169325">"የመተግበሪያ ትርፍ አረጋጋጭ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
index 6a4c7b2a..5408e2f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
@@ -226,7 +226,7 @@
     <string name="title_prefix" msgid="3991742709199357049">"標題"</string>
     <string name="list_template_demo_title" msgid="1740208242737246151">"「清單範本」示範"</string>
     <string name="long_msg_template_demo_title" msgid="1793748562161438131">"「長訊息範本」示範"</string>
-    <string name="long_msg_template_not_supported_text" msgid="3641559637317672505">"您的主機不支援「長訊息」範本"</string>
+    <string name="long_msg_template_not_supported_text" msgid="3641559637317672505">"你的主機不支援「長訊息」範本"</string>
     <string name="long_msg_template_not_supported_title" msgid="8600719470226274925">"主機不兼容"</string>
     <string name="msg_template_demo_title" msgid="3895210951340409473">"「訊息範本」示範"</string>
     <string name="msg_template_demo_text" msgid="2275291617716161409">"訊息會在這裡顯示。\n其他文字會在第二行顯示。"</string>
@@ -240,7 +240,7 @@
     <string name="google_sign_in" msgid="6556259799319701727">"Google 登入"</string>
     <string name="use_pin" msgid="7850893299484337431">"使用 PIN"</string>
     <string name="qr_code" msgid="5487041647280777397">"QR 碼"</string>
-    <string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"您的主機不支援登入範本"</string>
+    <string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"你的主機不支援登入範本"</string>
     <string name="sign_in_template_not_supported_title" msgid="4892883228898541764">"主機不兼容"</string>
     <string name="email_hint" msgid="7205549445477319606">"電郵"</string>
     <string name="sign_in_title" msgid="4551967308262681703">"登入"</string>
@@ -254,7 +254,7 @@
     <string name="qr_code_sign_in_title" msgid="8137070561006464518">"掃瞄 QR 碼即登入"</string>
     <string name="sign_in_with_google_title" msgid="8043752000786977249">"使用 Google 帳戶登入"</string>
     <string name="provider_sign_in_instruction" msgid="7586815688292506743">"使用這個按鈕可完成 Google 登入程序"</string>
-    <string name="sign_in_complete_text" msgid="8423984266325680606">"您已登入!"</string>
+    <string name="sign_in_complete_text" msgid="8423984266325680606">"你已登入!"</string>
     <string name="sign_in_complete_title" msgid="8919868148773983428">"已完成登入"</string>
     <string name="sign_in_template_demo_title" msgid="6052035424941410249">"「登入範本」示範"</string>
     <string name="tab_template_layouts_demo_title" msgid="6529681462424538165">"分頁範本示範"</string>
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"一些其他文字"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"可選取的清單範本"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"「工作限制」示範"</string>
-    <string name="task_limit_reached_msg" msgid="7162842196382260992">"這會導致步數超過上限,並將您向到新畫面。"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"這會導致步數超過上限,並將你向到新畫面。"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"點擊即可繼續使用"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"請查看各種範本,並確保汽車處於駕駛模式"</string>
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index c8ce9a4..ebb7251 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -660,6 +660,10 @@
     method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
   }
 
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+    method public String getContentId();
+  }
+
   @androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
     method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
     method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
@@ -1064,6 +1068,60 @@
     method public androidx.car.app.model.ItemList getItemList();
   }
 
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+    method public String getContentId();
+    method public androidx.car.app.model.CarIcon getIcon();
+    method public androidx.car.app.model.CarText getTitle();
+  }
+
+  public static final class Tab.Builder {
+    ctor public Tab.Builder();
+    ctor public Tab.Builder(androidx.car.app.model.Tab);
+    method public androidx.car.app.model.Tab build();
+    method public androidx.car.app.model.Tab.Builder setContentId(String);
+    method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+    method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+    method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+    method public String getContentId();
+    method public androidx.car.app.model.Template getTemplate();
+    field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+  }
+
+  public static final class TabContents.Builder {
+    ctor public TabContents.Builder(androidx.car.app.model.Template);
+    method public androidx.car.app.model.TabContents build();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+    method public String getActiveTabContentId();
+    method public androidx.car.app.model.Action getHeaderAction();
+    method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+    method public androidx.car.app.model.TabContents getTabContents();
+    method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+    method public boolean isLoading();
+  }
+
+  public static final class TabTemplate.Builder {
+    ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+    ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+    method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+    method public androidx.car.app.model.TabTemplate build();
+    method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+    method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+    method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+    method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+  }
+
+  public static interface TabTemplate.TabCallback {
+    method public default void onTabSelected(String);
+  }
+
   @androidx.car.app.annotations.CarProtocol public interface Template {
   }
 
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 02f26c19..0263505 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -866,7 +866,7 @@
 
 package androidx.car.app.messaging.model {
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class CarMessage {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class CarMessage {
     method public androidx.car.app.model.CarText getBody();
     method public long getReceivedTimeEpochMillis();
     method public androidx.core.app.Person? getSender();
@@ -887,12 +887,12 @@
     method public void onTextReply(String);
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public interface ConversationCallbackDelegate {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public interface ConversationCallbackDelegate {
     method public void sendMarkAsRead(androidx.car.app.OnDoneCallback);
     method public void sendTextReply(String, androidx.car.app.OnDoneCallback);
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class ConversationItem implements androidx.car.app.model.Item {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
     method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
@@ -1097,7 +1097,7 @@
     method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
     method public String getContentId();
   }
 
@@ -1525,7 +1525,7 @@
     method public androidx.car.app.model.ItemList getItemList();
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
     method public String getContentId();
     method public androidx.car.app.model.CarIcon getIcon();
     method public androidx.car.app.model.CarText getTitle();
@@ -1540,11 +1540,11 @@
     method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
     method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
     method public String getContentId();
     method public androidx.car.app.model.Template getTemplate();
     field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
@@ -1555,7 +1555,7 @@
     method public androidx.car.app.model.TabContents build();
   }
 
-  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
     method public String getActiveTabContentId();
     method public androidx.car.app.model.Action getHeaderAction();
     method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index c8ce9a4..ebb7251 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -660,6 +660,10 @@
     method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
   }
 
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+    method public String getContentId();
+  }
+
   @androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
     method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
     method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
@@ -1064,6 +1068,60 @@
     method public androidx.car.app.model.ItemList getItemList();
   }
 
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+    method public String getContentId();
+    method public androidx.car.app.model.CarIcon getIcon();
+    method public androidx.car.app.model.CarText getTitle();
+  }
+
+  public static final class Tab.Builder {
+    ctor public Tab.Builder();
+    ctor public Tab.Builder(androidx.car.app.model.Tab);
+    method public androidx.car.app.model.Tab build();
+    method public androidx.car.app.model.Tab.Builder setContentId(String);
+    method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+    method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+    method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+    method public String getContentId();
+    method public androidx.car.app.model.Template getTemplate();
+    field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+  }
+
+  public static final class TabContents.Builder {
+    ctor public TabContents.Builder(androidx.car.app.model.Template);
+    method public androidx.car.app.model.TabContents build();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+    method public String getActiveTabContentId();
+    method public androidx.car.app.model.Action getHeaderAction();
+    method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+    method public androidx.car.app.model.TabContents getTabContents();
+    method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+    method public boolean isLoading();
+  }
+
+  public static final class TabTemplate.Builder {
+    ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+    ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+    method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+    method public androidx.car.app.model.TabTemplate build();
+    method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+    method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+    method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+    method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+  }
+
+  public static interface TabTemplate.TabCallback {
+    method public default void onTabSelected(String);
+  }
+
   @androidx.car.app.annotations.CarProtocol public interface Template {
   }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
index 034b5de..e81089d 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
@@ -36,7 +36,7 @@
 /** Represents a single message in a {@link ConversationItem} */
 @ExperimentalCarApi
 @CarProtocol
-@RequiresCarApi(6)
+@RequiresCarApi(7)
 @KeepFields
 public class CarMessage {
     @Nullable
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegate.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegate.java
index 924e566..d9593c4 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegate.java
@@ -27,7 +27,7 @@
 /** Used by the host to invoke {@link ConversationCallback} methods on the client */
 @ExperimentalCarApi
 @CarProtocol
-@RequiresCarApi(6)
+@RequiresCarApi(7)
 public interface ConversationCallbackDelegate {
 
     /** Called from the host to invoke {@link ConversationCallback#onMarkAsRead()} on the client. */
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegateImpl.java
index 5ab50b0..ffadaa1 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationCallbackDelegateImpl.java
@@ -44,7 +44,7 @@
 @ExperimentalCarApi
 @RestrictTo(LIBRARY)
 @CarProtocol
-@RequiresCarApi(6)
+@RequiresCarApi(7)
 @KeepFields
 class ConversationCallbackDelegateImpl implements ConversationCallbackDelegate {
 
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
index b63b93e..c3962ef 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -43,7 +43,7 @@
 @ExperimentalCarApi
 @CarProtocol
 @KeepFields
-@RequiresCarApi(6)
+@RequiresCarApi(7)
 public class ConversationItem implements Item {
     @NonNull
     private final String mId;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Content.java b/car/app/app/src/main/java/androidx/car/app/model/Content.java
index 5023c429..c493b66 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Content.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Content.java
@@ -18,12 +18,10 @@
 
 import androidx.annotation.NonNull;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 
 /** Interface implemented by models that can be invalidated and refreshed individually. */
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 public interface Content {
 
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Tab.java b/car/app/app/src/main/java/androidx/car/app/model/Tab.java
index d99fd60..f0292db 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Tab.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Tab.java
@@ -21,7 +21,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.constraints.CarIconConstraints;
@@ -34,7 +33,6 @@
  * display tab headers.
  */
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @KeepFields
 public final class Tab implements Content {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
index ddb229b..6cf0bcb 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
@@ -21,7 +21,6 @@
 import androidx.annotation.NonNull;
 import androidx.car.app.OnDoneCallback;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 
 /**
@@ -29,7 +28,6 @@
  * {@link androidx.car.app.model.TabTemplate.TabCallback} events to the car app.
  */
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 public interface TabCallbackDelegate {
     /**
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
index 4e559ca..ed7a988 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
@@ -23,16 +23,14 @@
 import android.annotation.SuppressLint;
 import android.os.RemoteException;
 
-import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.IOnDoneCallback;
 import androidx.car.app.OnDoneCallback;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
-import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.annotations.KeepFields;
+import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.utils.RemoteUtils;
 
 /**
@@ -42,7 +40,6 @@
  */
 @RestrictTo(LIBRARY)
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @KeepFields
 public class TabCallbackDelegateImpl implements TabCallbackDelegate {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
index d27dc30..b16cbd3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
@@ -23,10 +23,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.constraints.TabContentsConstraints;
-import androidx.car.app.annotations.KeepFields;
 
 import java.util.Objects;
 
@@ -34,7 +33,6 @@
  * Represents the contents to display for a selected tab in a {@link TabTemplate}.
  */
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @KeepFields
 public class TabContents implements Content {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
index 1a55793..e216793 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
@@ -26,7 +26,6 @@
 import androidx.annotation.Nullable;
 import androidx.car.app.Screen;
 import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.constraints.TabsConstraints;
@@ -55,7 +54,6 @@
  * </ul>
  */
 @CarProtocol
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @KeepFields
 public class TabTemplate implements Template {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index edf1329..a269ea2 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -25,7 +25,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.Action.ActionType;
@@ -161,7 +160,6 @@
 
     /** Constraints for TabTemplate. */
     @NonNull
-    @ExperimentalCarApi
     @RequiresCarApi(6)
     public static final ActionsConstraints ACTIONS_CONSTRAINTS_TABS =
             new ActionsConstraints.Builder(ACTIONS_CONSTRAINTS_HEADER)
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
index b1d917901..664eaa0 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
@@ -18,7 +18,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.CarText;
 import androidx.car.app.model.GridTemplate;
@@ -37,7 +36,6 @@
  *
  * @hide
  */
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TabContentsConstraints {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
index d80ab48..83bcf62 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
@@ -18,7 +18,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
-import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.Tab;
 
@@ -31,7 +30,6 @@
  *
  * @hide
  */
-@ExperimentalCarApi
 @RequiresCarApi(6)
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TabsConstraints {
diff --git a/collection/collection/api/current.txt b/collection/collection/api/current.txt
index 3fbf783..a16f02c8 100644
--- a/collection/collection/api/current.txt
+++ b/collection/collection/api/current.txt
@@ -63,16 +63,16 @@
     method public void addLast(E element);
     method public void clear();
     method public operator E get(int index);
-    method public E! getFirst();
-    method public E! getLast();
+    method public E getFirst();
+    method public E getLast();
     method public boolean isEmpty();
     method public E popFirst();
     method public E popLast();
     method public void removeFromEnd(int count);
     method public void removeFromStart(int count);
     method public int size();
-    property public final E! first;
-    property public final E! last;
+    property public final E first;
+    property public final E last;
   }
 
   public final class CircularIntArray {
diff --git a/collection/collection/api/public_plus_experimental_current.txt b/collection/collection/api/public_plus_experimental_current.txt
index 3fbf783..a16f02c8 100644
--- a/collection/collection/api/public_plus_experimental_current.txt
+++ b/collection/collection/api/public_plus_experimental_current.txt
@@ -63,16 +63,16 @@
     method public void addLast(E element);
     method public void clear();
     method public operator E get(int index);
-    method public E! getFirst();
-    method public E! getLast();
+    method public E getFirst();
+    method public E getLast();
     method public boolean isEmpty();
     method public E popFirst();
     method public E popLast();
     method public void removeFromEnd(int count);
     method public void removeFromStart(int count);
     method public int size();
-    property public final E! first;
-    property public final E! last;
+    property public final E first;
+    property public final E last;
   }
 
   public final class CircularIntArray {
diff --git a/collection/collection/api/restricted_current.txt b/collection/collection/api/restricted_current.txt
index 3fbf783..a16f02c8 100644
--- a/collection/collection/api/restricted_current.txt
+++ b/collection/collection/api/restricted_current.txt
@@ -63,16 +63,16 @@
     method public void addLast(E element);
     method public void clear();
     method public operator E get(int index);
-    method public E! getFirst();
-    method public E! getLast();
+    method public E getFirst();
+    method public E getLast();
     method public boolean isEmpty();
     method public E popFirst();
     method public E popLast();
     method public void removeFromEnd(int count);
     method public void removeFromStart(int count);
     method public int size();
-    property public final E! first;
-    property public final E! last;
+    property public final E first;
+    property public final E last;
   }
 
   public final class CircularIntArray {
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index ac05bf2..ae892a1 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -12,8 +12,8 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T? getUpperBound();
-    method public T! getValue();
-    method public T! getVelocity();
+    method public T getValue();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -25,8 +25,8 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T? upperBound;
-    property public final T! value;
-    property public final T! velocity;
+    property public final T value;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -104,7 +104,7 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public androidx.compose.animation.core.AnimationState<T,V> toAnimationState();
@@ -115,7 +115,7 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -140,7 +140,7 @@
     method public long getLastFrameTimeNanos();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     property public final long finishedTimeNanos;
@@ -148,7 +148,7 @@
     property public final long lastFrameTimeNanos;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -567,7 +567,7 @@
 
   @androidx.compose.runtime.Stable public final class Transition<S> {
     method public java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> getAnimations();
-    method public S! getCurrentState();
+    method public S getCurrentState();
     method public String? getLabel();
     method public androidx.compose.animation.core.Transition.Segment<S> getSegment();
     method public S getTargetState();
@@ -575,7 +575,7 @@
     method public java.util.List<androidx.compose.animation.core.Transition<?>> getTransitions();
     method public boolean isRunning();
     property public final java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> animations;
-    property public final S! currentState;
+    property public final S currentState;
     property public final boolean isRunning;
     property public final String? label;
     property public final androidx.compose.animation.core.Transition.Segment<S> segment;
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index 03897cb..7a2c3a4 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -12,8 +12,8 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T? getUpperBound();
-    method public T! getValue();
-    method public T! getVelocity();
+    method public T getValue();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -25,8 +25,8 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T? upperBound;
-    property public final T! value;
-    property public final T! velocity;
+    property public final T value;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -104,7 +104,7 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public androidx.compose.animation.core.AnimationState<T,V> toAnimationState();
@@ -115,7 +115,7 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -140,7 +140,7 @@
     method public long getLastFrameTimeNanos();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     property public final long finishedTimeNanos;
@@ -148,7 +148,7 @@
     property public final long lastFrameTimeNanos;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -573,7 +573,7 @@
 
   @androidx.compose.runtime.Stable public final class Transition<S> {
     method public java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> getAnimations();
-    method public S! getCurrentState();
+    method public S getCurrentState();
     method public String? getLabel();
     method public androidx.compose.animation.core.Transition.Segment<S> getSegment();
     method public S getTargetState();
@@ -581,7 +581,7 @@
     method public java.util.List<androidx.compose.animation.core.Transition<?>> getTransitions();
     method public boolean isRunning();
     property public final java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> animations;
-    property public final S! currentState;
+    property public final S currentState;
     property public final boolean isRunning;
     property public final String? label;
     property public final androidx.compose.animation.core.Transition.Segment<S> segment;
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index fa302aa..b2e9658 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -12,8 +12,8 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T? getUpperBound();
-    method public T! getValue();
-    method public T! getVelocity();
+    method public T getValue();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -25,8 +25,8 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T? upperBound;
-    property public final T! value;
-    property public final T! velocity;
+    property public final T value;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -104,7 +104,7 @@
     method public T getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     method public androidx.compose.animation.core.AnimationState<T,V> toAnimationState();
@@ -115,7 +115,7 @@
     property public final T targetValue;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public final T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -140,7 +140,7 @@
     method public long getLastFrameTimeNanos();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T getValue();
-    method public T! getVelocity();
+    method public T getVelocity();
     method public V getVelocityVector();
     method public boolean isRunning();
     property public final long finishedTimeNanos;
@@ -148,7 +148,7 @@
     property public final long lastFrameTimeNanos;
     property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
     property public T value;
-    property public final T! velocity;
+    property public final T velocity;
     property public final V velocityVector;
   }
 
@@ -568,7 +568,7 @@
   @androidx.compose.runtime.Stable public final class Transition<S> {
     ctor @kotlin.PublishedApi internal Transition(androidx.compose.animation.core.MutableTransitionState<S> transitionState, optional String? label);
     method public java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> getAnimations();
-    method public S! getCurrentState();
+    method public S getCurrentState();
     method public String? getLabel();
     method public androidx.compose.animation.core.Transition.Segment<S> getSegment();
     method public S getTargetState();
@@ -576,7 +576,7 @@
     method public java.util.List<androidx.compose.animation.core.Transition<?>> getTransitions();
     method public boolean isRunning();
     property public final java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> animations;
-    property public final S! currentState;
+    property public final S currentState;
     property public final boolean isRunning;
     property public final String? label;
     property public final androidx.compose.animation.core.Transition.Segment<S> segment;
diff --git a/compose/compiler/compiler-hosted/integration-tests/build.gradle b/compose/compiler/compiler-hosted/integration-tests/build.gradle
index 149e956..0ac6b78 100644
--- a/compose/compiler/compiler-hosted/integration-tests/build.gradle
+++ b/compose/compiler/compiler-hosted/integration-tests/build.gradle
@@ -32,9 +32,7 @@
 
     testCompileOnly(libs.kotlinCompiler)
     testRuntimeOnly(
-        project(
-           ":compose:compiler:compiler-hosted:integration-tests:kotlin-compiler-repackaged"
-        )
+        project(":compose:compiler:compiler-hosted:integration-tests:kotlin-compiler-repackaged")
     )
 
     // tools.jar required for com.sun.jdi
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index 9eb33b2..c991670 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -112,6 +112,7 @@
             9701 to "1.5.0-alpha01",
             9801 to "1.5.0-alpha02",
             9901 to "1.5.0-alpha03",
+            10001 to "1.5.0-alpha04",
         )
 
         /**
@@ -124,7 +125,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.4.6"
+        const val compilerVersion: String = "1.5.0-alpha04"
         private val minimumRuntimeVersion: String
             get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
     }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ArrangementTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ArrangementTest.kt
new file mode 100644
index 0000000..8808486
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ArrangementTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.layout
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SmallTest
+@RunWith(Parameterized::class)
+class ArrangementTest(private val testParam: TestParam) {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun testArrangement() = with(testParam) {
+        with(actualArrangement) {
+            composeTestRule.density.arrange(
+                actualTotalSize,
+                actualSizes,
+                actualLayoutDirection,
+                actualOutPositions
+            )
+
+            assertThat(actualOutPositions).isEqualTo(expectedOutPositions)
+        }
+    }
+
+    @Suppress("ArrayInDataClass") // Used only in parameterized tests.
+    data class TestParam(
+        val actualArrangement: Arrangement.HorizontalOrVertical,
+        val actualTotalSize: Int,
+        val actualSizes: IntArray,
+        val actualLayoutDirection: LayoutDirection,
+        val actualOutPositions: IntArray,
+        val expectedOutPositions: IntArray,
+    )
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun data() = listOf<Any>(
+            TestParam(
+                actualArrangement = Arrangement.SpaceBetween,
+                actualTotalSize = 2376,
+                actualSizes = intArrayOf(108, 24),
+                actualLayoutDirection = LayoutDirection.Rtl,
+                actualOutPositions = intArrayOf(0, 0),
+                expectedOutPositions = intArrayOf(2268, 0),
+            ),
+            TestParam(
+                actualArrangement = Arrangement.SpaceBetween,
+                actualTotalSize = 2376,
+                actualSizes = intArrayOf(108),
+                actualLayoutDirection = LayoutDirection.Rtl,
+                actualOutPositions = intArrayOf(0),
+                expectedOutPositions = intArrayOf(2268),
+            ),
+            TestParam(
+                actualArrangement = Arrangement.SpaceBetween,
+                actualTotalSize = 2376,
+                actualSizes = intArrayOf(108, 24),
+                actualLayoutDirection = LayoutDirection.Ltr,
+                actualOutPositions = intArrayOf(0, 0),
+                expectedOutPositions = intArrayOf(0, 2352),
+            ),
+            TestParam(
+                actualArrangement = Arrangement.SpaceBetween,
+                actualTotalSize = 2376,
+                actualSizes = intArrayOf(108),
+                actualLayoutDirection = LayoutDirection.Ltr,
+                actualOutPositions = intArrayOf(0),
+                expectedOutPositions = intArrayOf(0),
+            ),
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnModifierTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnModifierTest.kt
new file mode 100644
index 0000000..ae3fbd6
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnModifierTest.kt
@@ -0,0 +1,442 @@
+/*
+ * 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.layout
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measured
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RowColumnModifierTest() {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun testRow_updatesOnAlignmentChange() {
+        var positionInParentY = 0f
+        var alignment by mutableStateOf(Alignment.Top)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(100.toDp())) {
+                    Row(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        20.toDp(), if (index == 4) {
+                                            10.toDp()
+                                        } else {
+                                            20.toDp()
+                                        }
+                                    )
+                                    .align(alignment)
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentY = positionInParent.y
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentY).isEqualTo(0)
+            alignment = Alignment.CenterVertically
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentY).isEqualTo(5)
+        }
+    }
+
+    @Test
+    fun testRow_updatesOnAlignByBlockChange() {
+        var positionInParentY = 0f
+        val alignByBlock: (Measured) -> Int = { _ -> 5 }
+        val alignByNewBlock: (Measured) -> Int = { _ -> 0 }
+        var alignment by mutableStateOf(alignByBlock)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(100.toDp())) {
+                    Row(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        20.toDp(), if (index == 4) {
+                                            10.toDp()
+                                        } else {
+                                            20.toDp()
+                                        }
+                                    )
+                                    .alignBy(
+                                        if (index == 4) {
+                                            alignment
+                                        } else {
+                                            alignByBlock
+                                        }
+                                    )
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentY = positionInParent.y
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentY).isEqualTo(0)
+            alignment = alignByNewBlock
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentY).isEqualTo(5)
+        }
+    }
+
+    @Test
+    fun testRow_updatesOnWeightChange() {
+        var width = 0
+        var fill by mutableStateOf(false)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    Row(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        20.toDp()
+                                    )
+                                    .weight(1f, fill)
+                                    .onSizeChanged {
+                                        if (index > 0) {
+                                            Truth
+                                                .assertThat(it.width)
+                                                .isEqualTo(width)
+                                        } else {
+                                            width = it.width
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(width).isEqualTo(20)
+            fill = true
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(width).isEqualTo(40)
+        }
+    }
+
+    @Test
+    fun testRow_updatesOnWeightAndAlignmentChange() {
+        var width = 0
+        var fill by mutableStateOf(false)
+        var positionInParentY = 0f
+        var alignment by mutableStateOf(Alignment.Top)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    Row(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                .size(
+                                    20.toDp(), if (index == 4) {
+                                        10.toDp()
+                                    } else {
+                                        20.toDp()
+                                    }
+                                )
+                                .weight(1f, fill)
+                                .onSizeChanged {
+                                    if (index > 0) {
+                                        Truth
+                                            .assertThat(it.width)
+                                            .isEqualTo(width)
+                                    } else {
+                                        width = it.width
+                                    }
+                                }
+                                .align(alignment)
+                                .onPlaced {
+                                    if (index == 4) {
+                                        val positionInParent = it.positionInParent()
+                                        positionInParentY = positionInParent.y
+                                    }
+                                })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(width).isEqualTo(20)
+            Truth.assertThat(positionInParentY).isEqualTo(0)
+            alignment = Alignment.CenterVertically
+            fill = true
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(width).isEqualTo(40)
+            Truth.assertThat(positionInParentY).isEqualTo(5)
+        }
+    }
+
+    @Test
+    fun testColumn_updatesOnAlignmentChange() {
+        var positionInParentX = 0f
+        var alignment by mutableStateOf(Alignment.Start)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(100.toDp())) {
+                    Column(
+                        Modifier
+                            .wrapContentWidth()
+                            .wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        if (index == 4) {
+                                            10.toDp()
+                                        } else {
+                                            20.toDp()
+                                        },
+                                        20.toDp(),
+                                    )
+                                    .align(alignment)
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentX = positionInParent.x
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentX).isEqualTo(0)
+            alignment = Alignment.CenterHorizontally
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentX).isEqualTo(5)
+        }
+    }
+
+    @Test
+    fun testColumn_updatesOnAlignByBlockChange() {
+        var positionInParentX = 0f
+        val alignByBlock: (Measured) -> Int = { _ -> 5 }
+        val alignByNewBlock: (Measured) -> Int = { _ -> 0 }
+        var alignment by mutableStateOf(alignByBlock)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(100.toDp())) {
+                    Column(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        if (index == 4) {
+                                            10.toDp()
+                                        } else {
+                                            20.toDp()
+                                        }, 20.toDp()
+                                    )
+                                    .alignBy(
+                                        if (index == 4) {
+                                            alignment
+                                        } else {
+                                            alignByBlock
+                                        }
+                                    )
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentX = positionInParent.x
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentX).isEqualTo(0)
+            alignment = alignByNewBlock
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(positionInParentX).isEqualTo(5)
+        }
+    }
+
+    @Test
+    fun testColumn_updatesOnWeightChange() {
+        var height = 0
+        var fill by mutableStateOf(false)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    Column(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                    .size(
+                                        20.toDp()
+                                    )
+                                    .weight(1f, fill)
+                                    .onSizeChanged {
+                                        if (index > 0) {
+                                            Truth
+                                                .assertThat(it.height)
+                                                .isEqualTo(height)
+                                        } else {
+                                            height = it.height
+                                        }
+                                    })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(height).isEqualTo(20)
+            fill = true
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(height).isEqualTo(40)
+        }
+    }
+
+    @Test
+    fun testColumn_updatesOnWeightAndAlignmentChange() {
+        var height = 0
+        var fill by mutableStateOf(false)
+        var positionInParentX = 0f
+        var alignment by mutableStateOf(Alignment.Start)
+
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    Column(
+                        Modifier.wrapContentHeight()
+                    ) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier
+                                .size(
+                                    if (index == 4) {
+                                        10.toDp()
+                                    } else {
+                                        20.toDp()
+                                    },
+                                    20.toDp(),
+                                )
+                                .weight(1f, fill)
+                                .onSizeChanged {
+                                    if (index > 0) {
+                                        Truth
+                                            .assertThat(it.height)
+                                            .isEqualTo(height)
+                                    } else {
+                                        height = it.height
+                                    }
+                                }
+                                .align(alignment)
+                                .onPlaced {
+                                    if (index == 4) {
+                                        val positionInParent = it.positionInParent()
+                                        positionInParentX = positionInParent.x
+                                    }
+                                })
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(height).isEqualTo(20)
+            Truth.assertThat(positionInParentX).isEqualTo(0)
+            alignment = Alignment.CenterHorizontally
+            fill = true
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(height).isEqualTo(40)
+            Truth.assertThat(positionInParentX).isEqualTo(5)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
index 8613205..80bd902 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
@@ -48,6 +48,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.roundToInt
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
@@ -56,15 +61,11 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import kotlin.math.max
-import kotlin.math.min
-import kotlin.math.roundToInt
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class RowColumnTest : LayoutTest() {
+
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
@@ -2540,7 +2541,51 @@
     }
 
     @Test
-    fun testRow_withSpaceBetweenArrangement() = with(density) {
+    fun testRow_withSpaceBetweenArrangement_singleItem() = with(density) {
+        val sizeDp = 50.toDp()
+
+        val drawLatch = CountDownLatch(2)
+        val childPosition = arrayOf(Offset(-1f, -1f))
+        val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
+        var parentLayoutCoordinates: LayoutCoordinates? = null
+        show {
+            Center {
+                Row(
+                    Modifier.fillMaxWidth().onGloballyPositioned { coordinates ->
+                        parentLayoutCoordinates = coordinates
+                        drawLatch.countDown()
+                    },
+                    horizontalArrangement = Arrangement.SpaceBetween
+                ) {
+                    for (i in 0 until childPosition.size) {
+                        Container(
+                            width = sizeDp,
+                            height = sizeDp,
+                            modifier = Modifier.onGloballyPositioned { coordinates ->
+                                childLayoutCoordinates[i] = coordinates
+                                drawLatch.countDown()
+                            }
+                        ) {
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+
+        calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(
+            Offset(0f, 0f),
+            childPosition[0]
+        )
+    }
+
+    @Test
+    fun testRow_withSpaceBetweenArrangement_multipleItems() = with(density) {
         val sizeDp = 50.toDp()
         val size = sizeDp.roundToPx()
 
@@ -2559,7 +2604,7 @@
                     },
                     horizontalArrangement = Arrangement.SpaceBetween
                 ) {
-                    for (i in 0 until childPosition.size) {
+                    for (i in childPosition.indices) {
                         Container(
                             width = sizeDp,
                             height = sizeDp,
@@ -4513,7 +4558,55 @@
     }
 
     @Test
-    fun testRow_Rtl_arrangementSpaceBetween() = with(density) {
+    fun testRow_Rtl_arrangementSpaceBetween_singleItem() = with(density) {
+        val size = 100
+        val sizeDp = size.toDp()
+
+        val drawLatch = CountDownLatch(2)
+        val childPosition = Array(1) { Offset.Zero }
+        val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
+        var parentLayoutCoordinates: LayoutCoordinates? = null
+        show {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .onGloballyPositioned { coordinates ->
+                            parentLayoutCoordinates = coordinates
+                            drawLatch.countDown()
+                        },
+                    horizontalArrangement = Arrangement.SpaceBetween
+                ) {
+                    for (i in childPosition.indices) {
+                        Container(
+                            width = sizeDp,
+                            height = sizeDp,
+                            modifier = Modifier.onGloballyPositioned { coordinates ->
+                                childLayoutCoordinates[i] = coordinates
+                                drawLatch.countDown()
+                            },
+                            content = {}
+                        )
+                    }
+                }
+            }
+        }
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+
+        calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        val gap = root.width - size.toFloat()
+        assertEquals(
+            Offset(gap, 0f),
+            childPosition[0]
+        )
+    }
+
+    @Test
+    fun testRow_Rtl_arrangementSpaceBetween_multipleItems() = with(density) {
         val size = 100
         val sizeDp = size.toDp()
 
@@ -5672,6 +5765,7 @@
             ValueElement("fill", false)
         )
     }
+
     @Test
     fun testColumn_AlignInspectableValue() {
         val modifier = with(ColumnScopeInstance) { Modifier.align(Alignment.Start) }
@@ -5701,9 +5795,7 @@
             ValueElement("fill", false)
         )
     }
-    // endregion
 }
-
 private val TestHorizontalLine = HorizontalAlignmentLine(::min)
 private val TestVerticalLine = VerticalAlignmentLine(::min)
 
@@ -5770,4 +5862,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
index 36aa00e..ca7cfe7 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
@@ -211,11 +211,10 @@
         )
     }
 
-    override fun update(node: AlignmentLineOffsetDpNode): AlignmentLineOffsetDpNode {
+    override fun update(node: AlignmentLineOffsetDpNode) {
         node.alignmentLine = alignmentLine
         node.before = before
         node.after = after
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -287,11 +286,10 @@
         return inspectorInfo()
     }
 
-    override fun update(node: AlignmentLineOffsetTextUnitNode): AlignmentLineOffsetTextUnitNode {
+    override fun update(node: AlignmentLineOffsetTextUnitNode) {
         node.alignmentLine = alignmentLine
         node.before = before
         node.after = after
-        return node
     }
 }
 
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
index 023db20..a408742 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
@@ -26,6 +26,7 @@
 import kotlin.math.min
 import kotlin.math.roundToInt
 import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
+
 /**
  * Used to specify the arrangement of the layout's children in layouts like [Row] or [Column] in
  * the main axis direction (horizontal and vertical, respectively).
@@ -664,13 +665,18 @@
         outPosition: IntArray,
         reverseInput: Boolean
     ) {
+        if (size.isEmpty()) return
+
         val consumedSize = size.fold(0) { a, b -> a + b }
-        val gapSize = if (size.size > 1) {
-            (totalSize - consumedSize).toFloat() / (size.size - 1)
-        } else {
-            0f
-        }
+        val noOfGaps = maxOf(size.lastIndex, 1)
+        val gapSize = (totalSize - consumedSize).toFloat() / noOfGaps
+
         var current = 0f
+        if (reverseInput && size.size == 1) {
+            // If the layout direction is right-to-left and there is only one gap,
+            // we start current with the gap size. That forces the single item to be right-aligned.
+            current = gapSize
+        }
         size.forEachIndexed(reverseInput) { index, it ->
             outPosition[index] = current.roundToInt()
             current += it.toFloat() + gapSize
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
index d42d7f0..bd8c46e 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
@@ -83,10 +83,9 @@
         )
     }
 
-    override fun update(node: AspectRatioNode): AspectRatioNode {
+    override fun update(node: AspectRatioNode) {
         node.aspectRatio = aspectRatio
         node.matchHeightConstraintsFirst = matchHeightConstraintsFirst
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() { inspectorInfo() }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Box.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Box.kt
index f1d4a80..c70e44d 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Box.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Box.kt
@@ -269,10 +269,9 @@
         return BoxChildDataNode(alignment, matchParentSize)
     }
 
-    override fun update(node: BoxChildDataNode): BoxChildDataNode {
+    override fun update(node: BoxChildDataNode) {
         node.alignment = alignment
         node.matchParentSize = matchParentSize
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
index 819c069..2a6ba7c 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
@@ -25,8 +25,6 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.Measured
 import androidx.compose.ui.layout.VerticalAlignmentLine
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
 
 /**
  * A layout composable that places its children in a vertical sequence. For a layout composable
@@ -201,49 +199,31 @@
     override fun Modifier.weight(weight: Float, fill: Boolean): Modifier {
         require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
         return this.then(
-            LayoutWeightImpl(
+            LayoutWeightElement(
                 weight = weight,
-                fill = fill,
-                inspectorInfo = debugInspectorInfo {
-                    name = "weight"
-                    value = weight
-                    properties["weight"] = weight
-                    properties["fill"] = fill
-                }
+                fill = fill
             )
         )
     }
 
     @Stable
     override fun Modifier.align(alignment: Alignment.Horizontal) = this.then(
-        HorizontalAlignModifier(
-            horizontal = alignment,
-            inspectorInfo = debugInspectorInfo {
-                name = "align"
-                value = alignment
-            }
+        HorizontalAlignElement(
+            horizontal = alignment
         )
     )
 
     @Stable
     override fun Modifier.alignBy(alignmentLine: VerticalAlignmentLine) = this.then(
-        SiblingsAlignedModifier.WithAlignmentLine(
-            alignmentLine = alignmentLine,
-            inspectorInfo = debugInspectorInfo {
-                name = "alignBy"
-                value = alignmentLine
-            }
+        WithAlignmentLineElement(
+            alignmentLine = alignmentLine
         )
     )
 
     @Stable
     override fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int) = this.then(
-        SiblingsAlignedModifier.WithAlignmentLineBlock(
-            block = alignmentLineBlock,
-            inspectorInfo = debugInspectorInfo {
-                name = "alignBy"
-                value = alignmentLineBlock
-            }
+        WithAlignmentLineBlockElement(
+            block = alignmentLineBlock
         )
     )
 }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
index e0df7f5..4fa34b4 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
@@ -151,10 +151,10 @@
         return OffsetNode(x, y, rtlAware)
     }
 
-    override fun update(node: OffsetNode): OffsetNode = node.also {
-        it.x = x
-        it.y = y
-        it.rtlAware = rtlAware
+    override fun update(node: OffsetNode) {
+        node.x = x
+        node.y = y
+        node.rtlAware = rtlAware
     }
 
     override fun equals(other: Any?): Boolean {
@@ -208,9 +208,9 @@
         return OffsetPxNode(offset, rtlAware)
     }
 
-    override fun update(node: OffsetPxNode): OffsetPxNode = node.also {
-        it.offset = offset
-        it.rtlAware = rtlAware
+    override fun update(node: OffsetPxNode) {
+        node.offset = offset
+        node.rtlAware = rtlAware
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
index b8bc7e1..60e3434 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
@@ -347,12 +347,12 @@
         return PaddingNode(start, top, end, bottom, rtlAware)
     }
 
-    override fun update(node: PaddingNode): PaddingNode = node.also {
-        it.start = start
-        it.top = top
-        it.end = end
-        it.bottom = bottom
-        it.rtlAware = rtlAware
+    override fun update(node: PaddingNode) {
+        node.start = start
+        node.top = top
+        node.end = end
+        node.bottom = bottom
+        node.rtlAware = rtlAware
     }
 
     override fun hashCode(): Int {
@@ -416,8 +416,8 @@
         return PaddingValuesModifier(paddingValues)
     }
 
-    override fun update(node: PaddingValuesModifier): PaddingValuesModifier = node.also {
-        it.paddingValues = paddingValues
+    override fun update(node: PaddingValuesModifier) {
+        node.paddingValues = paddingValues
     }
 
     override fun hashCode(): Int = paddingValues.hashCode()
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
index 7a99435..1c7b93b 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
@@ -16,18 +16,16 @@
 
 package androidx.compose.foundation.layout
 
-import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.HorizontalAlignmentLine
 import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Measured
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
 
 /**
  * A layout composable that places its children in a horizontal sequence. For a layout composable
@@ -221,38 +219,24 @@
     override fun Modifier.weight(weight: Float, fill: Boolean): Modifier {
         require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
         return this.then(
-            LayoutWeightImpl(
+            LayoutWeightElement(
                 weight = weight,
-                fill = fill,
-                inspectorInfo = debugInspectorInfo {
-                    name = "weight"
-                    value = weight
-                    properties["weight"] = weight
-                    properties["fill"] = fill
-                }
+                fill = fill
             )
         )
     }
 
     @Stable
     override fun Modifier.align(alignment: Alignment.Vertical) = this.then(
-        VerticalAlignModifier(
-            vertical = alignment,
-            inspectorInfo = debugInspectorInfo {
-                name = "align"
-                value = alignment
-            }
+        VerticalAlignElement(
+            alignment
         )
     )
 
     @Stable
     override fun Modifier.alignBy(alignmentLine: HorizontalAlignmentLine) = this.then(
-        SiblingsAlignedModifier.WithAlignmentLine(
-            alignmentLine = alignmentLine,
-            inspectorInfo = debugInspectorInfo {
-                name = "alignBy"
-                value = alignmentLine
-            }
+        WithAlignmentLineElement(
+            alignmentLine = alignmentLine
         )
     )
 
@@ -260,12 +244,8 @@
     override fun Modifier.alignByBaseline() = alignBy(FirstBaseline)
 
     override fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int) = this.then(
-        SiblingsAlignedModifier.WithAlignmentLineBlock(
-            block = alignmentLineBlock,
-            inspectorInfo = debugInspectorInfo {
-                name = "alignBy"
-                value = alignmentLineBlock
-            }
+        WithAlignmentLineBlockElement(
+            block = alignmentLineBlock
         )
     )
 }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
index 0d625d7..ce9028a 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -29,10 +30,10 @@
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.Measured
-import androidx.compose.ui.layout.ParentDataModifier
 import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
 import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
@@ -628,22 +629,24 @@
     return crossAxisMax
 }
 
-internal class LayoutWeightImpl(
+internal class LayoutWeightElement(
     val weight: Float,
     val fill: Boolean,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
-    override fun Density.modifyParentData(parentData: Any?) =
-        ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
-            it.weight = weight
-            it.fill = fill
-        }
+) : ModifierNodeElement<LayoutWeightNode>() {
+    override fun create(): LayoutWeightNode {
+        return LayoutWeightNode(weight, fill)
+    }
 
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        val otherModifier = other as? LayoutWeightImpl ?: return false
-        return weight == otherModifier.weight &&
-            fill == otherModifier.fill
+    override fun update(node: LayoutWeightNode) {
+        node.weight = weight
+        node.fill = fill
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "weight"
+        value = weight
+        properties["weight"] = weight
+        properties["fill"] = fill
     }
 
     override fun hashCode(): Int {
@@ -652,102 +655,168 @@
         return result
     }
 
-    override fun toString(): String =
-        "LayoutWeightImpl(weight=$weight, fill=$fill)"
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? LayoutWeightElement ?: return false
+        return weight == otherModifier.weight &&
+            fill == otherModifier.fill
+    }
 }
 
-internal sealed class SiblingsAlignedModifier(
-    inspectorInfo: InspectorInfo.() -> Unit
-) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
+internal class LayoutWeightNode(
+    var weight: Float,
+    var fill: Boolean,
+) : ParentDataModifierNode, Modifier.Node() {
+    override fun Density.modifyParentData(parentData: Any?) =
+        ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+            it.weight = weight
+            it.fill = fill
+        }
+}
+
+internal class WithAlignmentLineBlockElement(
+    val block: (Measured) -> Int
+) : ModifierNodeElement<SiblingsAlignedNode.WithAlignmentLineBlockNode>() {
+    override fun create(): SiblingsAlignedNode.WithAlignmentLineBlockNode {
+        return SiblingsAlignedNode.WithAlignmentLineBlockNode(block)
+    }
+
+    override fun update(node: SiblingsAlignedNode.WithAlignmentLineBlockNode) {
+        node.block = block
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? WithAlignmentLineBlockElement ?: return false
+        return block == otherModifier.block
+    }
+
+    override fun hashCode(): Int = block.hashCode()
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "alignBy"
+        value = block
+    }
+}
+
+internal class WithAlignmentLineElement(
+    val alignmentLine: AlignmentLine
+) : ModifierNodeElement<SiblingsAlignedNode.WithAlignmentLineNode>() {
+    override fun create(): SiblingsAlignedNode.WithAlignmentLineNode {
+        return SiblingsAlignedNode.WithAlignmentLineNode(alignmentLine)
+    }
+
+    override fun update(node: SiblingsAlignedNode.WithAlignmentLineNode) {
+        node.alignmentLine = alignmentLine
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "alignBy"
+        value = alignmentLine
+    }
+
+    override fun hashCode(): Int = alignmentLine.hashCode()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? WithAlignmentLineElement ?: return false
+        return alignmentLine == otherModifier.alignmentLine
+    }
+}
+
+internal sealed class SiblingsAlignedNode : ParentDataModifierNode, Modifier.Node() {
     abstract override fun Density.modifyParentData(parentData: Any?): Any?
 
-    internal class WithAlignmentLineBlock(
-        val block: (Measured) -> Int,
-        inspectorInfo: InspectorInfo.() -> Unit
-    ) : SiblingsAlignedModifier(inspectorInfo) {
+    internal class WithAlignmentLineBlockNode(
+        var block: (Measured) -> Int,
+    ) : SiblingsAlignedNode() {
         override fun Density.modifyParentData(parentData: Any?): Any {
             return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
                 it.crossAxisAlignment =
                     CrossAxisAlignment.Relative(AlignmentLineProvider.Block(block))
             }
         }
-
-        override fun equals(other: Any?): Boolean {
-            if (this === other) return true
-            val otherModifier = other as? WithAlignmentLineBlock ?: return false
-            return block == otherModifier.block
-        }
-
-        override fun hashCode(): Int = block.hashCode()
-
-        override fun toString(): String = "WithAlignmentLineBlock(block=$block)"
     }
 
-    internal class WithAlignmentLine(
-        val alignmentLine: AlignmentLine,
-        inspectorInfo: InspectorInfo.() -> Unit
-    ) : SiblingsAlignedModifier(inspectorInfo) {
+    internal class WithAlignmentLineNode(
+        var alignmentLine: AlignmentLine,
+    ) : SiblingsAlignedNode() {
         override fun Density.modifyParentData(parentData: Any?): Any {
             return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
                 it.crossAxisAlignment =
                     CrossAxisAlignment.Relative(AlignmentLineProvider.Value(alignmentLine))
             }
         }
-
-        override fun equals(other: Any?): Boolean {
-            if (this === other) return true
-            val otherModifier = other as? WithAlignmentLine ?: return false
-            return alignmentLine == otherModifier.alignmentLine
-        }
-
-        override fun hashCode(): Int = alignmentLine.hashCode()
-
-        override fun toString(): String = "WithAlignmentLine(line=$alignmentLine)"
     }
 }
 
-internal class HorizontalAlignModifier(
-    val horizontal: Alignment.Horizontal,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
+internal class HorizontalAlignElement(
+    val horizontal: Alignment.Horizontal
+) : ModifierNodeElement<HorizontalAlignNode>() {
+    override fun create(): HorizontalAlignNode {
+        return HorizontalAlignNode(horizontal)
+    }
+
+    override fun update(node: HorizontalAlignNode) {
+        node.horizontal = horizontal
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "align"
+        value = horizontal
+    }
+    override fun hashCode(): Int = horizontal.hashCode()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? HorizontalAlignElement ?: return false
+        return horizontal == otherModifier.horizontal
+    }
+}
+
+internal class HorizontalAlignNode(
+    var horizontal: Alignment.Horizontal
+) : ParentDataModifierNode, Modifier.Node() {
     override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
         return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
             it.crossAxisAlignment = CrossAxisAlignment.horizontal(horizontal)
         }
     }
+}
+
+internal class VerticalAlignElement(
+    val alignment: Alignment.Vertical,
+) : ModifierNodeElement<VerticalAlignNode>() {
+    override fun create(): VerticalAlignNode {
+        return VerticalAlignNode(alignment)
+    }
+
+    override fun update(node: VerticalAlignNode) {
+        node.vertical = alignment
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "align"
+        value = alignment
+    }
+
+    override fun hashCode(): Int = alignment.hashCode()
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        val otherModifier = other as? HorizontalAlignModifier ?: return false
-        return horizontal == otherModifier.horizontal
+        val otherModifier = other as? VerticalAlignElement ?: return false
+        return alignment == otherModifier.alignment
     }
-
-    override fun hashCode(): Int = horizontal.hashCode()
-
-    override fun toString(): String =
-        "HorizontalAlignModifier(horizontal=$horizontal)"
 }
 
-internal class VerticalAlignModifier(
-    val vertical: Alignment.Vertical,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
+internal class VerticalAlignNode(
+    var vertical: Alignment.Vertical
+) : ParentDataModifierNode, Modifier.Node() {
     override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
         return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
             it.crossAxisAlignment = CrossAxisAlignment.vertical(vertical)
         }
     }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        val otherModifier = other as? VerticalAlignModifier ?: return false
-        return vertical == otherModifier.vertical
-    }
-
-    override fun hashCode(): Int = vertical.hashCode()
-
-    override fun toString(): String =
-        "VerticalAlignModifier(vertical=$vertical)"
 }
 
 /**
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
index 3fd65398..78210e2 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
@@ -615,9 +615,9 @@
 ) : ModifierNodeElement<FillNode>() {
     override fun create(): FillNode = FillNode(direction = direction, fraction = fraction)
 
-    override fun update(node: FillNode): FillNode = node.also {
-        it.direction = direction
-        it.fraction = fraction
+    override fun update(node: FillNode) {
+        node.direction = direction
+        node.fraction = fraction
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -723,12 +723,12 @@
             enforceIncoming = enforceIncoming
         )
 
-    override fun update(node: SizeNode): SizeNode = node.also {
-        it.minWidth = minWidth
-        it.minHeight = minHeight
-        it.maxWidth = maxWidth
-        it.maxHeight = maxHeight
-        it.enforceIncoming = enforceIncoming
+    override fun update(node: SizeNode) {
+        node.minWidth = minWidth
+        node.minHeight = minHeight
+        node.maxWidth = maxWidth
+        node.maxHeight = maxHeight
+        node.enforceIncoming = enforceIncoming
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -903,10 +903,10 @@
         alignmentCallback
     )
 
-    override fun update(node: WrapContentNode): WrapContentNode = node.also {
-        it.direction = direction
-        it.unbounded = unbounded
-        it.alignmentCallback = alignmentCallback
+    override fun update(node: WrapContentNode) {
+        node.direction = direction
+        node.unbounded = unbounded
+        node.alignmentCallback = alignmentCallback
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -1029,9 +1029,9 @@
         minHeight = minHeight
     )
 
-    override fun update(node: UnspecifiedConstraintsNode) = node.also {
-        it.minWidth = minWidth
-        it.minHeight = minHeight
+    override fun update(node: UnspecifiedConstraintsNode) {
+        node.minWidth = minWidth
+        node.minHeight = minHeight
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 2a1b29f..337b887 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -494,6 +494,11 @@
     method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
+  public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+    ctor public GridCells.FixedSize(float size);
+    method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+  }
+
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
     method public int getCurrentLineSpan();
   }
@@ -631,7 +636,7 @@
     property public abstract long size;
   }
 
-  public sealed interface LazyStaggeredGridItemScope {
+  @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
   }
 
   public sealed interface LazyStaggeredGridLayoutInfo {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 1bd8088..1143e67 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -371,8 +371,12 @@
 
 package androidx.compose.foundation.gestures.snapping {
 
+  public final class LazyGridSnapLayoutInfoProviderKt {
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+  }
+
   public final class LazyListSnapLayoutInfoProviderKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.unit.Density,? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
     method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
   }
 
@@ -392,6 +396,16 @@
     method public float calculateSnappingOffset(androidx.compose.ui.unit.Density, float currentVelocity);
   }
 
+  @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
+    method public int position(androidx.compose.ui.unit.Density, int layoutSize, int itemSize, int itemIndex);
+    field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
+  }
+
+  public static final class SnapPositionInLayout.Companion {
+    method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
+    property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
+  }
+
 }
 
 package androidx.compose.foundation.interaction {
@@ -614,7 +628,7 @@
     method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
-  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+  public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
     ctor public GridCells.FixedSize(float size);
     method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
@@ -865,7 +879,8 @@
     property public abstract long size;
   }
 
-  public sealed interface LazyStaggeredGridItemScope {
+  @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
+    method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
   }
 
   public sealed interface LazyStaggeredGridLayoutInfo {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2a1b29f..337b887 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -494,6 +494,11 @@
     method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
+  public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+    ctor public GridCells.FixedSize(float size);
+    method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+  }
+
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
     method public int getCurrentLineSpan();
   }
@@ -631,7 +636,7 @@
     property public abstract long size;
   }
 
-  public sealed interface LazyStaggeredGridItemScope {
+  @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
   }
 
   public sealed interface LazyStaggeredGridLayoutInfo {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
index 74ae0fc..e0f379e 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
@@ -32,6 +32,7 @@
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkRecompose
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -48,7 +49,10 @@
     private var active = mutableStateOf(true)
     private var reuseKey = mutableStateOf(0)
 
-    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
+    private val style = TextStyle.Default.copy(
+        fontFamily = FontFamily.Monospace,
+        color = Color.Red,
+    )
 
     @Composable
     override fun MeasuredContent() {
@@ -57,6 +61,7 @@
                 Text(
                     toggleText.value,
                     style = style,
+                    color = style.color, /* for now, ignore color merge allocs */
                     modifier = Modifier.fillMaxWidth()
                 )
             }
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 b80442a..c7d9378 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
@@ -1004,13 +1004,15 @@
                                 StaggeredGridItemSpan.FullLine
                             else
                                 StaggeredGridItemSpan.SingleLane
-                        }
+                        },
+                        key = { indices.value[it % indices.value.size] }
                     ) {
                         var expanded by remember { mutableStateOf(false) }
                         val index = indices.value[it % indices.value.size]
                         val color = colors[index]
                         Box(
                             modifier = Modifier
+                                .animateItemPlacement()
                                 .height(if (!expanded) heights[index] else heights[index] * 2)
                                 .border(2.dp, color, RoundedCornerShape(5.dp))
                                 .clickable {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnappingDemos.kt
index e4e5e15..7b7ec83 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnappingDemos.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
index 8fccbc9..203eae5 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
@@ -20,9 +20,11 @@
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
 import androidx.compose.ui.unit.Density
+import kotlin.math.abs
 import kotlin.math.ceil
 import kotlin.math.floor
 import kotlin.math.roundToInt
+import kotlin.math.sign
 
 @OptIn(ExperimentalFoundationApi::class)
 fun SnapLayoutInfoProvider(
@@ -56,4 +58,31 @@
     }
 
     override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
+}
+
+internal fun calculateFinalOffset(velocity: Float, lowerBound: Float, upperBound: Float): Float {
+
+    fun Float.isValidDistance(): Boolean {
+        return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
+    }
+
+    val finalDistance = when (sign(velocity)) {
+        0f -> {
+            if (abs(upperBound) <= abs(lowerBound)) {
+                upperBound
+            } else {
+                lowerBound
+            }
+        }
+
+        1f -> upperBound
+        -1f -> lowerBound
+        else -> 0f
+    }
+
+    return if (finalDistance.isValidDistance()) {
+        finalDistance
+    } else {
+        0f
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
index 65eb312..1603fdc 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
@@ -30,10 +30,12 @@
 import androidx.compose.runtime.withFrameMillis
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 
+private val style = TextStyle(color = Color.Magenta)
 /**
  * These demos are for using the memory profiler to observe initial compo and recompo memory
  * pressure.
@@ -107,7 +109,10 @@
 @Composable
 fun IfNotEmptyText(text: State<String>) {
     if (text.value.isNotEmpty()) {
-        Text(text.value)
+        Text(
+            text.value,
+            style = style
+        )
     }
 }
 
@@ -118,6 +123,7 @@
         ReusableContent(active.value.second) {
             Text(
                 "Some static text",
+                style = style,
                 modifier = Modifier.fillMaxWidth()
             )
         }
@@ -126,7 +132,7 @@
 
 @Composable
 private fun SetText(text: State<String>) {
-    Text(text.value)
+    Text(text.value, style = style)
 }
 
 @Composable
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
index 4d8b7f9..9cb4e69 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
@@ -359,19 +360,14 @@
 
     @Test
     fun draggable_callsDragStop_whenNewState() {
-        var total = 0f
         var dragStopped = 0f
-        val state = mutableStateOf(
-            DraggableState { total += it }
-        )
+        val state = mutableStateOf(DraggableState { })
         setDraggableContent {
-            if (total < 20f) {
-                Modifier.draggable(
-                    orientation = Orientation.Horizontal,
-                    onDragStopped = { dragStopped += 1 },
-                    state = state.value
-                )
-            } else Modifier
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDragStopped = { dragStopped += 1 },
+                state = state.value
+            )
         }
         rule.onNodeWithTag(draggableBoxTag).performTouchInput {
             down(center)
@@ -387,6 +383,180 @@
     }
 
     @Test
+    fun draggable_callsDragStop_whenNewOrientation() {
+        var dragStopped = 0f
+        var orientation by mutableStateOf(Orientation.Horizontal)
+        setDraggableContent {
+            Modifier.draggable(
+                orientation = orientation,
+                onDragStopped = { dragStopped += 1 },
+                onDrag = {}
+            )
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(0f)
+            orientation = Orientation.Vertical
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun draggable_callsDragStop_whenDisabled() {
+        var dragStopped = 0f
+        var enabled by mutableStateOf(true)
+        setDraggableContent {
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDragStopped = { dragStopped += 1 },
+                enabled = enabled,
+                onDrag = {}
+            )
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(0f)
+            enabled = false
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun draggable_callsDragStop_whenNewReverseDirection() {
+        var dragStopped = 0f
+        var reverseDirection by mutableStateOf(false)
+        setDraggableContent {
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDragStopped = { dragStopped += 1 },
+                onDrag = {},
+                reverseDirection = reverseDirection
+            )
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(0f)
+            reverseDirection = true
+        }
+        rule.runOnIdle {
+            assertThat(dragStopped).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun draggable_updates_startDragImmediately() {
+        var total = 0f
+        var startDragImmediately by mutableStateOf(false)
+        var touchSlop: Float? = null
+        setDraggableContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDrag = { total += it },
+                startDragImmediately = startDragImmediately
+            )
+        }
+        val delta = touchSlop!! / 2f
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(delta, 0f))
+            up()
+        }
+        rule.runOnIdle {
+            assertThat(total).isEqualTo(0f)
+            startDragImmediately = true
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(delta, 0f))
+            up()
+        }
+        rule.runOnIdle {
+            assertThat(total).isEqualTo(delta)
+        }
+    }
+
+    @Test
+    fun draggable_updates_onDragStarted() {
+        var total = 0f
+        var onDragStarted1Calls = 0
+        var onDragStarted2Calls = 0
+        var onDragStarted: (Offset) -> Unit by mutableStateOf({ onDragStarted1Calls += 1 })
+        setDraggableContent {
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDrag = { total += it },
+                onDragStarted = onDragStarted
+            )
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+            up()
+        }
+        rule.runOnIdle {
+            assertThat(onDragStarted1Calls).isEqualTo(1)
+            assertThat(onDragStarted2Calls).isEqualTo(0)
+            onDragStarted = { onDragStarted2Calls += 1 }
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+            up()
+        }
+        rule.runOnIdle {
+            assertThat(onDragStarted1Calls).isEqualTo(1)
+            assertThat(onDragStarted2Calls).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun draggable_updates_onDragStopped() {
+        var total = 0f
+        var onDragStopped1Calls = 0
+        var onDragStopped2Calls = 0
+        var onDragStopped: (Float) -> Unit by mutableStateOf({ onDragStopped1Calls += 1 })
+        setDraggableContent {
+            Modifier.draggable(
+                orientation = Orientation.Horizontal,
+                onDrag = { total += it },
+                onDragStopped = onDragStopped
+            )
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+        }
+        rule.runOnIdle {
+            assertThat(onDragStopped1Calls).isEqualTo(0)
+            assertThat(onDragStopped2Calls).isEqualTo(0)
+            onDragStopped = { onDragStopped2Calls += 1 }
+        }
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            up()
+        }
+        rule.runOnIdle {
+            // We changed the lambda before we ever stopped dragging, so only the new one should be
+            // called
+            assertThat(onDragStopped1Calls).isEqualTo(0)
+            assertThat(onDragStopped2Calls).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun draggable_resumesNormally_whenInterruptedWithHigherPriority() = runBlocking {
         var total = 0f
         var dragStopped = 0f
@@ -576,6 +746,112 @@
         }
     }
 
+    @Test
+    fun draggable_interactionSource_resetWhenEnabledChanged() {
+        val interactionSource = MutableInteractionSource()
+        val enabledState = mutableStateOf(true)
+
+        var scope: CoroutineScope? = null
+
+        setDraggableContent {
+            scope = rememberCoroutineScope()
+            Modifier.draggable(
+                Orientation.Horizontal,
+                enabled = enabledState.value,
+                interactionSource = interactionSource
+            ) {}
+        }
+
+        val interactions = mutableListOf<Interaction>()
+
+        scope!!.launch {
+            interactionSource.interactions.collect { interactions.add(it) }
+        }
+
+        rule.runOnIdle {
+            assertThat(interactions).isEmpty()
+        }
+
+        rule.onNodeWithTag(draggableBoxTag)
+            .performTouchInput {
+                down(Offset(visibleSize.width / 4f, visibleSize.height / 2f))
+                moveBy(Offset(visibleSize.width / 2f, 0f))
+            }
+
+        rule.runOnIdle {
+            assertThat(interactions).hasSize(1)
+            assertThat(interactions.first()).isInstanceOf(DragInteraction.Start::class.java)
+        }
+
+        rule.runOnIdle {
+            enabledState.value = false
+        }
+
+        rule.runOnIdle {
+            assertThat(interactions).hasSize(2)
+            assertThat(interactions.first()).isInstanceOf(DragInteraction.Start::class.java)
+            assertThat(interactions[1]).isInstanceOf(DragInteraction.Cancel::class.java)
+            assertThat((interactions[1] as DragInteraction.Cancel).start)
+                .isEqualTo(interactions[0])
+        }
+    }
+
+    @Test
+    fun draggable_interactionSource_resetWhenInteractionSourceChanged() {
+        val interactionSource1 = MutableInteractionSource()
+        val interactionSource2 = MutableInteractionSource()
+        val interactionSourceState = mutableStateOf(interactionSource1)
+
+        var scope: CoroutineScope? = null
+
+        setDraggableContent {
+            scope = rememberCoroutineScope()
+            Modifier.draggable(
+                Orientation.Horizontal,
+                interactionSource = interactionSourceState.value
+            ) {}
+        }
+
+        val interactions1 = mutableListOf<Interaction>()
+        val interactions2 = mutableListOf<Interaction>()
+
+        scope!!.launch {
+            interactionSource1.interactions.collect { interactions1.add(it) }
+        }
+
+        rule.runOnIdle {
+            assertThat(interactions1).isEmpty()
+            assertThat(interactions2).isEmpty()
+        }
+
+        rule.onNodeWithTag(draggableBoxTag)
+            .performTouchInput {
+                down(Offset(visibleSize.width / 4f, visibleSize.height / 2f))
+                moveBy(Offset(visibleSize.width / 2f, 0f))
+            }
+
+        rule.runOnIdle {
+            assertThat(interactions1).hasSize(1)
+            assertThat(interactions1.first()).isInstanceOf(DragInteraction.Start::class.java)
+            assertThat(interactions2).isEmpty()
+        }
+
+        rule.runOnIdle {
+            interactionSourceState.value = interactionSource2
+        }
+
+        rule.runOnIdle {
+            assertThat(interactions1).hasSize(2)
+            assertThat(interactions1.first()).isInstanceOf(DragInteraction.Start::class.java)
+            assertThat(interactions1[1]).isInstanceOf(DragInteraction.Cancel::class.java)
+            assertThat((interactions1[1] as DragInteraction.Cancel).start)
+                .isEqualTo(interactions1[0])
+            // Currently we don't emit drag start for an in progress drag, but this might change
+            // in the future.
+            assertThat(interactions2).isEmpty()
+        }
+    }
+
     @OptIn(ExperimentalTestApi::class)
     @Test
     fun draggable_cancelMidDown_shouldContinueWithNextDown() {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt
new file mode 100644
index 0000000..39f045c
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt
@@ -0,0 +1,515 @@
+/*
+ * 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.gesture.snapping
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
+import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion.CenterToCenter
+import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.BaseLazyGridTestWithOrientation
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.test.swipeWithVelocity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@OptIn(ExperimentalFoundationApi::class)
+class LazyGridSnapFlingBehaviorTest(private val orientation: Orientation) :
+    BaseLazyGridTestWithOrientation(orientation) {
+
+    private val density: Density
+        get() = rule.density
+
+    private lateinit var snapLayoutInfoProvider: SnapLayoutInfoProvider
+    private lateinit var snapFlingBehavior: FlingBehavior
+
+    @Test
+    fun belowThresholdVelocity_lessThanAnItemScroll_shouldStayInSamePage() {
+        var lazyGridState: LazyGridState? = null
+        var stepSize = 0f
+        var velocityThreshold = 0f
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState().also { lazyGridState = it }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state)
+        }
+
+        // Scroll a bit
+        onMainList().swipeOnMainAxis()
+        rule.waitForIdle()
+        val currentItem = density.getCurrentSnappedItem(lazyGridState)
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(stepSize / 2, velocityThreshold / 2)
+        }
+
+        // assert
+        rule.runOnIdle {
+            val nextItem = density.getCurrentSnappedItem(lazyGridState)
+            assertEquals(currentItem, nextItem)
+        }
+    }
+
+    @Test
+    fun belowThresholdVelocity_moreThanAnItemScroll_shouldGoToNextPage() {
+        var lazyGridState: LazyGridState? = null
+        var stepSize = 0f
+        var velocityThreshold = 0f
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState().also { lazyGridState = it }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state)
+        }
+
+        // Scroll a bit
+        onMainList().swipeOnMainAxis()
+        rule.waitForIdle()
+        val currentItem = density.getCurrentSnappedItem(lazyGridState)
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                stepSize,
+                velocityThreshold / 2
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            val nextItem = density.getCurrentSnappedItem(lazyGridState)
+            assertEquals(nextItem, currentItem + (lazyGridState?.maxCells() ?: 0))
+        }
+    }
+
+    @Test
+    fun aboveThresholdVelocityForward_notLargeEnoughScroll_shouldGoToNextPage() {
+        var lazyGridState: LazyGridState? = null
+        var stepSize = 0f
+        var velocityThreshold = 0f
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState().also { lazyGridState = it }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state)
+        }
+
+        // Scroll a bit
+        onMainList().swipeOnMainAxis()
+        rule.waitForIdle()
+        val currentItem = density.getCurrentSnappedItem(lazyGridState)
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                stepSize / 2,
+                velocityThreshold * 2
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            val nextItem = density.getCurrentSnappedItem(lazyGridState)
+            assertEquals(nextItem, currentItem + (lazyGridState?.maxCells() ?: 0))
+        }
+    }
+
+    @Test
+    fun aboveThresholdVelocityBackward_notLargeEnoughScroll_shouldGoToPreviousPage() {
+        var lazyGridState: LazyGridState? = null
+        var stepSize = 0f
+        var velocityThreshold = 0f
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState().also { lazyGridState = it }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state)
+        }
+
+        // Scroll a bit
+        onMainList().swipeOnMainAxis()
+        rule.waitForIdle()
+        val currentItem = density.getCurrentSnappedItem(lazyGridState)
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                stepSize / 2,
+                velocityThreshold * 2,
+                true
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            val nextItem = density.getCurrentSnappedItem(lazyGridState)
+            assertEquals(nextItem, currentItem - (lazyGridState?.maxCells() ?: 0))
+        }
+    }
+
+    @Test
+    fun aboveThresholdVelocity_largeEnoughScroll_shouldGoToNextNextPage() {
+        var lazyGridState: LazyGridState? = null
+        var stepSize = 0f
+        var velocityThreshold = 0f
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState().also { lazyGridState = it }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state)
+        }
+
+        // Scroll a bit
+        onMainList().swipeOnMainAxis()
+        rule.waitForIdle()
+        val currentItem = density.getCurrentSnappedItem(lazyGridState)
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                1.5f * stepSize,
+                velocityThreshold * 3
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            val nextItem = density.getCurrentSnappedItem(lazyGridState)
+            assertEquals(nextItem, currentItem + 2 * (lazyGridState?.maxCells() ?: 0))
+        }
+    }
+
+    @Test
+    fun performFling_shouldPropagateVelocityIfHitEdges() {
+        var stepSize = 0f
+        var latestAvailableVelocity = Velocity.Zero
+        lateinit var lazyGridState: LazyGridState
+        val inspectingNestedScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+                latestAvailableVelocity = available
+                return Velocity.Zero
+            }
+        }
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            lazyGridState = rememberLazyGridState(180) // almost at the end
+            stepSize = with(density) { ItemSize.toPx() }
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .nestedScroll(inspectingNestedScrollConnection)
+            ) {
+                MainLayout(state = lazyGridState)
+            }
+        }
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                1.5f * stepSize,
+                30000f
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertNotEquals(latestAvailableVelocity.toAbsoluteFloat(), 0f)
+        }
+
+        // arrange
+        rule.runOnIdle {
+            runBlocking {
+                lazyGridState.scrollToItem(20) // almost at the start
+            }
+        }
+
+        latestAvailableVelocity = Velocity.Zero
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                -1.5f * stepSize,
+                30000f
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertNotEquals(latestAvailableVelocity.toAbsoluteFloat(), 0f)
+        }
+    }
+
+    @Test
+    fun performFling_shouldConsumeAllVelocityIfInTheMiddleOfTheList() {
+        var stepSize = 0f
+        var latestAvailableVelocity = Velocity.Zero
+        lateinit var lazyGridState: LazyGridState
+        val inspectingNestedScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+                latestAvailableVelocity = available
+                return Velocity.Zero
+            }
+        }
+
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            lazyGridState = rememberLazyGridState(100) // middle of the grid
+            stepSize = with(density) { ItemSize.toPx() }
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .nestedScroll(inspectingNestedScrollConnection)
+            ) {
+                MainLayout(state = lazyGridState)
+            }
+        }
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                1.5f * stepSize,
+                10000f // use a not so high velocity
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertEquals(latestAvailableVelocity.toAbsoluteFloat(), 0f)
+        }
+
+        // arrange
+        rule.runOnIdle {
+            runBlocking {
+                lazyGridState.scrollToItem(100) // return to the middle
+            }
+        }
+
+        latestAvailableVelocity = Velocity.Zero
+
+        // act
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                -1.5f * stepSize,
+                10000f // use a not so high velocity
+            )
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertEquals(latestAvailableVelocity.toAbsoluteFloat(), 0f)
+        }
+    }
+
+    @Test
+    fun remainingScrollOffset_shouldFollowAnimationOffsets() {
+        var stepSize = 0f
+        var velocityThreshold = 0f
+        val scrollOffset = mutableListOf<Float>()
+        // arrange
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState()
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
+            MainLayout(state = state, scrollOffset)
+        }
+
+        rule.mainClock.autoAdvance = false
+        // act
+        val velocity = velocityThreshold * 3
+        onMainList().performTouchInput {
+            swipeMainAxisWithVelocity(
+                1.5f * stepSize,
+                velocity
+            )
+        }
+        rule.mainClock.advanceTimeByFrame()
+
+        // assert
+        val initialTargetOffset =
+            with(snapLayoutInfoProvider) { density.calculateApproachOffset(velocity) }
+        Truth.assertThat(scrollOffset.first { it != 0f }).isWithin(0.5f)
+            .of(initialTargetOffset)
+
+        // act: wait for remaining offset to grow instead of decay, this indicates the last
+        // snap step will start
+        rule.mainClock.advanceTimeUntil {
+            scrollOffset.size > 2 &&
+                scrollOffset.last() > scrollOffset[scrollOffset.lastIndex - 1]
+        }
+
+        // assert: next calculated offset is the first value emitted by remainingScrollOffset
+        val finalRemainingOffset = with(snapLayoutInfoProvider) {
+            density.calculateSnappingOffset(10000f)
+        }
+        Truth.assertThat(scrollOffset.last()).isWithin(0.5f)
+            .of(finalRemainingOffset)
+        rule.mainClock.autoAdvance = true
+
+        // assert: value settles back to zero
+        rule.runOnIdle {
+            Truth.assertThat(scrollOffset.last()).isEqualTo(0f)
+        }
+    }
+
+    private fun onMainList() = rule.onNodeWithTag(TestTag)
+
+    @Composable
+    fun MainLayout(state: LazyGridState, scrollOffset: MutableList<Float> = mutableListOf()) {
+        snapLayoutInfoProvider = remember(state) { SnapLayoutInfoProvider(state) }
+        val innerFlingBehavior =
+            rememberSnapFlingBehavior(snapLayoutInfoProvider = snapLayoutInfoProvider)
+        snapFlingBehavior = remember(innerFlingBehavior) {
+            QuerySnapFlingBehavior(innerFlingBehavior) { scrollOffset.add(it) }
+        }
+        LazyGrid(
+            cells = GridCells.FixedSize(ItemSize),
+            state = state,
+            modifier = Modifier.testTag(TestTag),
+            flingBehavior = snapFlingBehavior
+        ) {
+            items(200) {
+                Box(modifier = Modifier
+                    .size(ItemSize)
+                    .background(Color.Yellow)) {
+                    BasicText(text = it.toString())
+                }
+            }
+        }
+    }
+
+    private fun LazyGridState.maxCells() =
+        if (layoutInfo.orientation == Orientation.Vertical) {
+            layoutInfo.visibleItemsInfo.maxOf { it.column }
+        } else {
+            layoutInfo.visibleItemsInfo.maxOf { it.row }
+        } + 1
+
+    private fun SemanticsNodeInteraction.swipeOnMainAxis() {
+        performTouchInput {
+            if (orientation == Orientation.Vertical) {
+                swipeUp()
+            } else {
+                swipeLeft()
+            }
+        }
+    }
+
+    private fun Density.getCurrentSnappedItem(state: LazyGridState?): Int {
+        var itemIndex = -1
+        if (state == null) return -1
+        var minDistance = Float.POSITIVE_INFINITY
+        (state.layoutInfo.visibleItemsInfo).forEach {
+            val distance = calculateDistanceToDesiredSnapPosition(
+                state.layoutInfo,
+                it,
+                CenterToCenter
+            )
+            if (abs(distance) < minDistance) {
+                minDistance = abs(distance)
+                itemIndex = it.index
+            }
+        }
+        return itemIndex
+    }
+
+    private fun TouchInjectionScope.swipeMainAxisWithVelocity(
+        scrollSize: Float,
+        endVelocity: Float,
+        reversed: Boolean = false
+    ) {
+        val (start, end) = if (orientation == Orientation.Vertical) {
+            bottomCenter to bottomCenter.copy(y = bottomCenter.y - scrollSize)
+        } else {
+            centerRight to centerRight.copy(x = centerRight.x - scrollSize)
+        }
+        swipeWithVelocity(
+            if (reversed) end else start,
+            if (reversed) start else end,
+            endVelocity
+        )
+    }
+
+    private fun Velocity.toAbsoluteFloat(): Float {
+        return (if (orientation == Orientation.Vertical) y else x).absoluteValue
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
+
+        val ItemSize = 200.dp
+        const val TestTag = "MainList"
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapLayoutInfoProviderTest.kt
new file mode 100644
index 0000000..4bbab00
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapLayoutInfoProviderTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gesture.snapping
+
+import androidx.compose.animation.core.calculateTargetValue
+import androidx.compose.animation.splineBasedDecay
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.BaseLazyGridTestWithOrientation
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import kotlin.math.absoluteValue
+import kotlin.math.round
+import kotlin.math.sign
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class LazyGridSnapLayoutInfoProviderTest(orientation: Orientation) :
+    BaseLazyGridTestWithOrientation(orientation) {
+
+    private val density: Density get() = rule.density
+
+    @Test
+    fun snapStepSize_sameSizeItems_shouldBeAverageItemSize() {
+        var expectedItemSize = 0f
+        var actualItemSize = 0f
+
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState()
+            val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
+                actualItemSize = with(it) { density.calculateSnapStepSize() }
+            }
+            expectedItemSize = with(density) { FixedItemSize.toPx() }
+            MainLayout(
+                state = state,
+                layoutInfo = layoutInfoProvider,
+                items = 200,
+                itemSizeProvider = { FixedItemSize }
+            )
+        }
+
+        rule.runOnIdle {
+            assertEquals(round(expectedItemSize), round(actualItemSize))
+        }
+    }
+
+    @Test
+    fun snapStepSize_differentSizeItems_shouldBeAverageItemSizeOnReferenceIndex() {
+        var actualItemSize = 0f
+        var expectedItemSize = 0f
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState()
+            val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
+                actualItemSize = with(it) { density.calculateSnapStepSize() }
+            }
+            expectedItemSize = state.layoutInfo.visibleItemsInfo.filter {
+                if (vertical) {
+                    it.column == 0
+                } else {
+                    it.row == 0
+                }
+            }.map {
+                if (vertical) it.size.height else it.size.width
+            }.average().toFloat()
+
+            MainLayout(state, layoutInfoProvider, DynamicItemSizes.size, { DynamicItemSizes[it] })
+        }
+
+        rule.runOnIdle {
+            assertEquals(round(expectedItemSize), round(actualItemSize))
+        }
+    }
+
+    @Test
+    fun snapStepSize_withSpacers_shouldBeAverageItemSize() {
+        var snapStepSize = 0f
+        var actualItemSize = 0f
+        rule.setContent {
+            val density = LocalDensity.current
+            val state = rememberLazyGridState()
+            val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
+                snapStepSize = with(it) { density.calculateSnapStepSize() }
+            }
+
+            actualItemSize = with(density) { (FixedItemSize + FixedItemSize / 2).toPx() }
+
+            MainLayout(
+                state = state,
+                layoutInfo = layoutInfoProvider,
+                items = 200,
+                itemSizeProvider = { FixedItemSize }) {
+                if (vertical) {
+                    Column {
+                        Box(
+                            modifier = Modifier
+                                .size(FixedItemSize)
+                                .background(Color.Red)
+                        )
+                        Spacer(
+                            modifier = Modifier
+                                .size(FixedItemSize / 2)
+                                .background(Color.Yellow)
+                        )
+                    }
+                } else {
+                    Row {
+                        Box(
+                            modifier = Modifier
+                                .size(FixedItemSize)
+                                .background(Color.Red)
+                        )
+                        Spacer(
+                            modifier = Modifier
+                                .size(FixedItemSize / 2)
+                                .background(Color.Yellow)
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertEquals(round(actualItemSize), round(snapStepSize))
+        }
+    }
+
+    @Test
+    fun calculateApproachOffset_highVelocity_approachOffsetIsEqualToDecayMinusItemSize() {
+        lateinit var layoutInfoProvider: SnapLayoutInfoProvider
+        val decay = splineBasedDecay<Float>(rule.density)
+        fun calculateTargetOffset(velocity: Float): Float {
+            val offset = decay.calculateTargetValue(0f, velocity).absoluteValue
+            return (offset - with(density) { 200.dp.toPx() }).coerceAtLeast(0f) * velocity.sign
+        }
+        rule.setContent {
+            val state = rememberLazyGridState()
+            layoutInfoProvider = remember(state) { createLayoutInfo(state) }
+            LazyGrid(
+                cells = GridCells.Fixed(3),
+                state = state,
+                flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
+            ) {
+                items(200) {
+                    Box(modifier = Modifier.size(200.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertEquals(
+                with(layoutInfoProvider) { density.calculateApproachOffset(10000f) },
+                calculateTargetOffset(10000f)
+            )
+            assertEquals(
+                with(layoutInfoProvider) { density.calculateApproachOffset(-10000f) },
+                calculateTargetOffset(-10000f)
+            )
+        }
+    }
+
+    @Test
+    fun calculateApproachOffset_lowVelocity_approachOffsetIsEqualToZero() {
+        lateinit var layoutInfoProvider: SnapLayoutInfoProvider
+        rule.setContent {
+            val state = rememberLazyGridState()
+            layoutInfoProvider = remember(state) { createLayoutInfo(state) }
+            LazyGrid(
+                cells = GridCells.Fixed(3),
+                state = state,
+                flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
+            ) {
+                items(200) {
+                    Box(modifier = Modifier.size(200.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertEquals(
+                with(layoutInfoProvider) { density.calculateApproachOffset(1000f) },
+                0f
+            )
+            assertEquals(
+                with(layoutInfoProvider) { density.calculateApproachOffset(-1000f) },
+                0f
+            )
+        }
+    }
+
+    @Composable
+    private fun MainLayout(
+        state: LazyGridState,
+        layoutInfo: SnapLayoutInfoProvider,
+        items: Int,
+        itemSizeProvider: (Int) -> Dp,
+        gridItem: @Composable (Int) -> Unit = { Box(Modifier.size(itemSizeProvider(it))) }
+    ) {
+        LazyGrid(
+            cells = GridCells.Fixed(3),
+            state = state,
+            flingBehavior = rememberSnapFlingBehavior(snapLayoutInfoProvider = layoutInfo)
+        ) {
+            items(items) { gridItem(it) }
+        }
+    }
+
+    private fun createLayoutInfo(
+        state: LazyGridState,
+    ): SnapLayoutInfoProvider {
+        return SnapLayoutInfoProvider(state)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
+
+        val FixedItemSize = 200.dp
+        val DynamicItemSizes = (200..500).map { it.dp }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
index 784b4c9f..3153d92 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
 import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion.CenterToCenter
 import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
 import androidx.compose.foundation.layout.Box
@@ -498,13 +499,11 @@
 
         val ItemSize = 200.dp
         const val TestTag = "MainList"
-        val CenterToCenter: Density.(Float, Float) -> Float =
-            { layoutSize, itemSize -> layoutSize / 2f - itemSize / 2f }
     }
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-private class QuerySnapFlingBehavior(
+internal class QuerySnapFlingBehavior(
     val snapFlingBehavior: SnapFlingBehavior,
     val onAnimationStep: (Float) -> Unit
 ) : FlingBehavior {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
index 61ce9f7..030e1d7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
@@ -760,7 +760,7 @@
             rule.onNodeWithTag("4").assertDoesNotExist()
             rule.onNodeWithTag("5").assertDoesNotExist()
             val item2Size = itemSize3 /* the real size of the item 2 */
-            // item 2 moves from and item 4 moves to `0 - item size`, right before the start edge
+            // item 2 moves from `0 - item size`, right before the start edge
             val startItem2Offset = -item2Size
             val item2Offset =
                 startItem2Offset + (itemSize2 - startItem2Offset) * fraction
@@ -833,7 +833,7 @@
         }
 
         onAnimationFrame { fraction ->
-            // item 1 moves from and item 8 moves to `gridSize`, right after the end edge
+            // item 8 moves from and item 2 moves to `gridSize`, right after the end edge
             val startItem8Offset = gridSize
             val endItem2Offset = gridSize
             val line4Size = itemSize3
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt
new file mode 100644
index 0000000..f0315ad
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt
@@ -0,0 +1,639 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.grid
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.BeyondBoundsLayout
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.After
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Before
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Below
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Left
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Right
+import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection.Rtl
+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
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalComposeUiApi::class)
+@MediumTest
+@RunWith(Parameterized::class)
+class LazyGridBeyondBoundsTest(param: Param) {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    // We need to wrap the inline class parameter in another class because Java can't instantiate
+    // the inline class.
+    class Param(
+        val beyondBoundsLayoutDirection: BeyondBoundsLayout.LayoutDirection,
+        val reverseLayout: Boolean,
+        val layoutDirection: LayoutDirection,
+    ) {
+        override fun toString() = "beyondBoundsLayoutDirection=$beyondBoundsLayoutDirection " +
+            "reverseLayout=$reverseLayout " +
+            "layoutDirection=$layoutDirection"
+    }
+
+    private val beyondBoundsLayoutDirection = param.beyondBoundsLayoutDirection
+    private val reverseLayout = param.reverseLayout
+    private val layoutDirection = param.layoutDirection
+    private val placedItems = mutableSetOf<Int>()
+    private var beyondBoundsLayout: BeyondBoundsLayout? = null
+    private lateinit var lazyGridState: LazyGridState
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun initParameters() = buildList {
+            for (beyondBoundsLayoutDirection in listOf(Left, Right, Above, Below, Before, After)) {
+                for (reverseLayout in listOf(false, true)) {
+                    for (layoutDirection in listOf(Ltr, Rtl)) {
+                        add(Param(beyondBoundsLayoutDirection, reverseLayout, layoutDirection))
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun onlyOneVisibleItemIsPlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 10.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0)
+            assertThat(visibleItems).containsExactly(0)
+        }
+    }
+
+    @Test
+    fun onlyTwoVisibleItemsArePlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 20.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0, 1)
+            assertThat(visibleItems).containsExactly(0, 1)
+        }
+    }
+
+    @Test
+    fun onlyThreeVisibleItemsArePlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0, 1, 2)
+            assertThat(visibleItems).containsExactly(0, 1, 2)
+        }
+    }
+
+    @Test
+    fun emptyLazyList_doesNotCrash() {
+        // Arrange.
+        var addItems by mutableStateOf(true)
+        lateinit var beyondBoundsLayoutRef: BeyondBoundsLayout
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+            if (addItems) {
+                item {
+                    Box(
+                        Modifier.modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            beyondBoundsLayoutRef = beyondBoundsLayout!!
+            addItems = false
+        }
+
+        // Act.
+        val hasMoreContent = rule.runOnIdle {
+            beyondBoundsLayoutRef.layout(beyondBoundsLayoutDirection) {
+                hasMoreContent
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(hasMoreContent).isFalse()
+        }
+    }
+
+    @Test
+    fun oneExtraItemBeyondVisibleBounds() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(Modifier
+                    .size(10.toDp())
+                    .onPlaced { placedItems += 5 }
+                    .modifierLocalConsumer {
+                        beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                    }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that the beyond bounds items are present.
+                if (expectedExtraItemsBeforeVisibleBounds()) {
+                    assertThat(placedItems).containsExactly(4, 5, 6, 7)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                } else {
+                    assertThat(placedItems).containsExactly(5, 6, 7, 8)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun oneExtraItemBeyondVisibleBounds_multipleCells() {
+        val itemSize = 50
+        val itemSizeDp = itemSize.toDp()
+        // Arrange.
+        rule.setLazyContent(cells = 2, size = itemSizeDp * 3, firstVisibleItem = 10) {
+            // item | item  | x5
+            // item | local | x1
+            // item | item  | x5
+            items(11) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp)
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(Modifier
+                    .size(itemSizeDp)
+                    .onPlaced { placedItems += 11 }
+                    .modifierLocalConsumer {
+                        beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                    }
+                )
+            }
+            items(10) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp)
+                        .onPlaced { placedItems += index + 12 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that the beyond bounds items are present.
+                if (expectedExtraItemsBeforeVisibleBounds()) {
+                    assertThat(placedItems).containsExactly(9, 10, 11, 12, 13, 14, 15)
+                    assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+                } else {
+                    assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15, 16)
+                    assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15)
+            assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+        }
+    }
+
+    @Test
+    fun twoExtraItemsBeyondVisibleBounds() {
+        // Arrange.
+        var extraItemCount = 2
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += 5 }
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                if (--extraItemCount > 0) {
+                    placedItems.clear()
+                    // Return null to continue the search.
+                    null
+                } else {
+                    // Assert that the beyond bounds items are present.
+                    if (expectedExtraItemsBeforeVisibleBounds()) {
+                        assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    } else {
+                        assertThat(placedItems).containsExactly(5, 6, 7, 8, 9)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    placedItems.clear()
+                    // Return true to stop the search.
+                    true
+                }
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun allBeyondBoundsItemsInSpecifiedDirection() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                        .onPlaced { placedItems += 5 }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index + 6
+                        }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                if (hasMoreContent) {
+                    placedItems.clear()
+                    // Just return null so that we keep adding more items till we reach the end.
+                    null
+                } else {
+                    // Assert that the beyond bounds items are present.
+                    if (expectedExtraItemsBeforeVisibleBounds()) {
+                        assertThat(placedItems).containsExactly(0, 1, 2, 3, 4, 5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    } else {
+                        assertThat(placedItems).containsExactly(5, 6, 7, 8, 9, 10)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    placedItems.clear()
+                    // Return true to end the search.
+                    true
+                }
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun beyondBoundsLayoutRequest_inDirectionPerpendicularToLazyListOrientation() {
+        // Arrange.
+        var beyondBoundsLayoutCount = 0
+        rule.setLazyContentInPerpendicularDirection(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += 5 }
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+            placedItems.clear()
+        }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                beyondBoundsLayoutCount++
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Above, Below -> {
+                        assertThat(placedItems).containsExactly(5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    Before, After -> {
+                        if (expectedExtraItemsBeforeVisibleBounds()) {
+                            assertThat(placedItems).containsExactly(4, 5, 6, 7)
+                            assertThat(visibleItems).containsExactly(5, 6, 7)
+                        } else {
+                            assertThat(placedItems).containsExactly(5, 6, 7, 8)
+                            assertThat(visibleItems).containsExactly(5, 6, 7)
+                        }
+                    }
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        rule.runOnIdle {
+            when (beyondBoundsLayoutDirection) {
+                Left, Right, Above, Below -> {
+                    assertThat(beyondBoundsLayoutCount).isEqualTo(0)
+                }
+                Before, After -> {
+                    assertThat(beyondBoundsLayoutCount).isEqualTo(1)
+
+                    // Assert that the beyond bounds items are removed.
+                    assertThat(placedItems).containsExactly(5, 6, 7)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                }
+                else -> error("Unsupported BeyondBoundsLayoutDirection")
+            }
+        }
+    }
+
+    @Test
+    fun returningNullDoesNotCauseInfiniteLoop() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index
+                        }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                        .onPlaced { placedItems += 5 }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index + 6
+                        }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        var count = 0
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that we don't keep iterating when there is no ending condition.
+                assertThat(count++).isLessThan(lazyGridState.layoutInfo.totalItemsCount)
+                placedItems.clear()
+                // Always return null to continue the search.
+                null
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    private fun ComposeContentTestRule.setLazyContent(
+        size: Dp,
+        firstVisibleItem: Int,
+        cells: Int = 1,
+        content: LazyGridScope.() -> Unit
+    ) {
+        setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                lazyGridState = rememberLazyGridState(firstVisibleItem)
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Before, After ->
+                        LazyHorizontalGrid(
+                            rows = GridCells.Fixed(cells),
+                            modifier = Modifier.size(size),
+                            state = lazyGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    Above, Below ->
+                        LazyVerticalGrid(
+                            columns = GridCells.Fixed(cells),
+                            modifier = Modifier.size(size),
+                            state = lazyGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    else -> unsupportedDirection()
+                }
+            }
+        }
+    }
+
+    private fun ComposeContentTestRule.setLazyContentInPerpendicularDirection(
+        size: Dp,
+        firstVisibleItem: Int,
+        content: LazyGridScope.() -> Unit
+    ) {
+        setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                lazyGridState = rememberLazyGridState(firstVisibleItem)
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Before, After ->
+                        LazyVerticalGrid(
+                            columns = GridCells.Fixed(1),
+                            modifier = Modifier.size(size),
+                            state = lazyGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    Above, Below ->
+                        LazyHorizontalGrid(
+                            rows = GridCells.Fixed(1),
+                            modifier = Modifier.size(size),
+                            state = lazyGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    else -> unsupportedDirection()
+                }
+            }
+        }
+    }
+
+    private fun Int.toDp(): Dp = with(rule.density) { toDp() }
+
+    private val visibleItems: List<Int>
+        get() = lazyGridState.layoutInfo.visibleItemsInfo.map { it.index }
+
+    private fun expectedExtraItemsBeforeVisibleBounds() = when (beyondBoundsLayoutDirection) {
+        Right -> if (layoutDirection == Ltr) reverseLayout else !reverseLayout
+        Left -> if (layoutDirection == Ltr) !reverseLayout else reverseLayout
+        Above -> !reverseLayout
+        Below -> reverseLayout
+        After -> false
+        Before -> true
+        else -> error("Unsupported BeyondBoundsDirection")
+    }
+
+    private fun unsupportedDirection(): Nothing = error(
+        "Lazy list does not support beyond bounds layout for the specified direction"
+    )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
index 3bf22a4..c868e03 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.core.FloatSpringSpec
 import androidx.compose.foundation.AutoTestFrameClock
 import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.width
@@ -65,22 +66,28 @@
             itemSizeDp = itemSizePx.toDp()
             containerSizeDp = itemSizeDp * 3
         }
+    }
+
+    private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
         rule.setContent {
             state = rememberLazyGridState()
             scope = rememberCoroutineScope()
-            TestContent()
+            TestContent(with(rule.density) { spacingPx.toDp() })
+        }
+        runBlocking {
+            assertBlock()
         }
     }
 
     @Test
-    fun setupWorks() {
+    fun setupWorks() = testScroll {
         assertThat(state.firstVisibleItemIndex).isEqualTo(0)
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         assertThat(state.firstVisibleItemIndex).isEqualTo(0)
     }
 
     @Test
-    fun scrollToItem() = runBlocking {
+    fun scrollToItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(2)
         }
@@ -95,7 +102,7 @@
     }
 
     @Test
-    fun scrollToItemWithOffset() = runBlocking {
+    fun scrollToItemWithOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(6, 10)
         }
@@ -104,7 +111,7 @@
     }
 
     @Test
-    fun scrollToItemWithNegativeOffset() = runBlocking {
+    fun scrollToItemWithNegativeOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(6, -10)
         }
@@ -114,7 +121,7 @@
     }
 
     @Test
-    fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+    fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount - 6, 10)
         }
@@ -123,7 +130,7 @@
     }
 
     @Test
-    fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+    fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(1, -(itemSizePx + 10))
         }
@@ -132,7 +139,7 @@
     }
 
     @Test
-    fun scrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+    fun scrollToItemWithIndexLargerThanItemsCount() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount + 4)
         }
@@ -140,7 +147,7 @@
     }
 
     @Test
-    fun animateScrollBy() = runBlocking {
+    fun animateScrollBy() = testScroll {
         val scrollDistance = 320
 
         val expectedLine = scrollDistance / itemSizePx // resolves to 3
@@ -155,7 +162,7 @@
     }
 
     @Test
-    fun animateScrollToItem() = runBlocking {
+    fun animateScrollToItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, 10)
         }
@@ -164,7 +171,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithOffset() = runBlocking {
+    fun animateScrollToItemWithOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(6, 10)
         }
@@ -173,7 +180,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithNegativeOffset() = runBlocking {
+    fun animateScrollToItemWithNegativeOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(6, -10)
         }
@@ -183,7 +190,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+    fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(itemsCount - 6, 10)
         }
@@ -192,7 +199,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+    fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(2, -(itemSizePx + 10))
         }
@@ -201,7 +208,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+    fun animateScrollToItemWithIndexLargerThanItemsCount() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(itemsCount + 2)
         }
@@ -209,42 +216,42 @@
     }
 
     @Test
-    fun animatePerFrameForwardToVisibleItem() {
+    fun animatePerFrameForwardToVisibleItem() = testScroll {
         assertSpringAnimation(toIndex = 4)
     }
 
     @Test
-    fun animatePerFrameForwardToVisibleItemWithOffset() {
+    fun animatePerFrameForwardToVisibleItemWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 4, toOffset = 35)
     }
 
     @Test
-    fun animatePerFrameForwardToNotVisibleItem() {
+    fun animatePerFrameForwardToNotVisibleItem() = testScroll {
         assertSpringAnimation(toIndex = 16)
     }
 
     @Test
-    fun animatePerFrameForwardToNotVisibleItemWithOffset() {
+    fun animatePerFrameForwardToNotVisibleItemWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 20, toOffset = 35)
     }
 
     @Test
-    fun animatePerFrameBackward() {
+    fun animatePerFrameBackward() = testScroll {
         assertSpringAnimation(toIndex = 2, fromIndex = 12)
     }
 
     @Test
-    fun animatePerFrameBackwardWithOffset() {
+    fun animatePerFrameBackwardWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 2, fromIndex = 10, fromOffset = 58)
     }
 
     @Test
-    fun animatePerFrameBackwardWithInitialOffset() {
+    fun animatePerFrameBackwardWithInitialOffset() = testScroll {
         assertSpringAnimation(toIndex = 0, toOffset = 40, fromIndex = 8)
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = runBlocking {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, -itemSizePx * 3)
         }
@@ -253,7 +260,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(10)
             state.animateScrollToItem(0, itemSizePx * 3)
@@ -263,14 +270,14 @@
     }
 
     @Test
-    fun canScrollForward() = runBlocking {
+    fun canScrollForward() = testScroll {
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         assertThat(state.canScrollForward).isTrue()
         assertThat(state.canScrollBackward).isFalse()
     }
 
     @Test
-    fun canScrollBackward() = runBlocking {
+    fun canScrollBackward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount)
         }
@@ -280,7 +287,7 @@
     }
 
     @Test
-    fun canScrollForwardAndBackward() = runBlocking {
+    fun canScrollForwardAndBackward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(10)
         }
@@ -289,11 +296,32 @@
         assertThat(state.canScrollBackward).isTrue()
     }
 
+    @Test
+    fun animatePerFrameForwardWithSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(toIndex = 16, spacingPx = 10)
+    }
+
+    @Test
+    fun animatePerFrameForwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(toIndex = 16, spacingPx = -10)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(toIndex = 2, fromIndex = 12, spacingPx = 10)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(toIndex = 2, fromIndex = 12, spacingPx = -10)
+    }
+
     private fun assertSpringAnimation(
         toIndex: Int,
         toOffset: Int = 0,
         fromIndex: Int = 0,
-        fromOffset: Int = 0
+        fromOffset: Int = 0,
+        spacingPx: Int = 0
     ) {
         if (fromIndex != 0 || fromOffset != 0) {
             rule.runOnIdle {
@@ -317,8 +345,9 @@
             Thread.sleep(5)
         }
 
-        val startOffset = (fromIndex / 2 * itemSizePx + fromOffset).toFloat()
-        val endOffset = (toIndex / 2 * itemSizePx + toOffset).toFloat()
+        val itemWSpacing = itemSizePx + spacingPx
+        val startOffset = (fromIndex / 2 * itemWSpacing + fromOffset).toFloat()
+        val endOffset = (toIndex / 2 * itemWSpacing + toOffset).toFloat()
         val spec = FloatSpringSpec()
 
         val duration =
@@ -330,7 +359,7 @@
             val expectedValue =
                 spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
             val actualValue =
-                (state.firstVisibleItemIndex / 2 * itemSizePx + state.firstVisibleItemScrollOffset)
+                state.firstVisibleItemIndex / 2 * itemWSpacing + state.firstVisibleItemScrollOffset
             assertWithMessage(
                 "On animation frame at $i index=${state.firstVisibleItemIndex} " +
                     "offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
@@ -346,9 +375,14 @@
     }
 
     @Composable
-    private fun TestContent() {
+    private fun TestContent(spacingDp: Dp) {
         if (vertical) {
-            LazyVerticalGrid(GridCells.Fixed(2), Modifier.height(containerSizeDp), state) {
+            LazyVerticalGrid(
+                GridCells.Fixed(2),
+                Modifier.height(containerSizeDp),
+                state,
+                verticalArrangement = Arrangement.spacedBy(spacingDp)
+            ) {
                 items(itemsCount) {
                     ItemContent()
                 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
index 8d2a237..bca1dc3 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.LazyRow
@@ -73,21 +74,27 @@
             itemSizeDp = itemSizePx.toDp()
             containerSizeDp = itemSizeDp * 3
         }
+    }
+
+    private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
         rule.setContent {
             state = rememberLazyListState()
             scope = rememberCoroutineScope()
-            TestContent()
+            TestContent(with(rule.density) { spacingPx.toDp() })
+        }
+        runBlocking {
+            assertBlock()
         }
     }
 
     @Test
-    fun setupWorks() {
+    fun setupWorks() = testScroll {
         assertThat(state.firstVisibleItemIndex).isEqualTo(0)
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
     }
 
     @Test
-    fun scrollToItem() = runBlocking {
+    fun scrollToItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(3)
         }
@@ -96,7 +103,7 @@
     }
 
     @Test
-    fun scrollToItemWithOffset() = runBlocking {
+    fun scrollToItemWithOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(3, 10)
         }
@@ -105,7 +112,7 @@
     }
 
     @Test
-    fun scrollToItemWithNegativeOffset() = runBlocking {
+    fun scrollToItemWithNegativeOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(3, -10)
         }
@@ -115,7 +122,7 @@
     }
 
     @Test
-    fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+    fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount - 3, 10)
         }
@@ -124,7 +131,7 @@
     }
 
     @Test
-    fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+    fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(1, -(itemSizePx + 10))
         }
@@ -133,7 +140,7 @@
     }
 
     @Test
-    fun scrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+    fun scrollToItemWithIndexLargerThanItemsCount() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount + 2)
         }
@@ -141,7 +148,7 @@
     }
 
     @Test
-    fun animateScrollBy() = runBlocking {
+    fun animateScrollBy() = testScroll {
         val scrollDistance = 320
 
         val expectedIndex = scrollDistance / itemSizePx // resolves to 3
@@ -155,7 +162,7 @@
     }
 
     @Test
-    fun animateScrollToItem() = runBlocking {
+    fun animateScrollToItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(5, 10)
         }
@@ -164,7 +171,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithOffset() = runBlocking {
+    fun animateScrollToItemWithOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(3, 10)
         }
@@ -173,7 +180,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithNegativeOffset() = runBlocking {
+    fun animateScrollToItemWithNegativeOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(3, -10)
         }
@@ -183,7 +190,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+    fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(itemsCount - 3, 10)
         }
@@ -192,7 +199,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+    fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(1, -(itemSizePx + 10))
         }
@@ -201,7 +208,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+    fun animateScrollToItemWithIndexLargerThanItemsCount() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(itemsCount + 2)
         }
@@ -209,42 +216,42 @@
     }
 
     @Test
-    fun animatePerFrameForwardToVisibleItem() {
+    fun animatePerFrameForwardToVisibleItem() = testScroll {
         assertSpringAnimation(toIndex = 2)
     }
 
     @Test
-    fun animatePerFrameForwardToVisibleItemWithOffset() {
+    fun animatePerFrameForwardToVisibleItemWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 2, toOffset = 35)
     }
 
     @Test
-    fun animatePerFrameForwardToNotVisibleItem() {
+    fun animatePerFrameForwardToNotVisibleItem() = testScroll {
         assertSpringAnimation(toIndex = 8)
     }
 
     @Test
-    fun animatePerFrameForwardToNotVisibleItemWithOffset() {
+    fun animatePerFrameForwardToNotVisibleItemWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 10, toOffset = 35)
     }
 
     @Test
-    fun animatePerFrameBackward() {
+    fun animatePerFrameBackward() = testScroll {
         assertSpringAnimation(toIndex = 1, fromIndex = 6)
     }
 
     @Test
-    fun animatePerFrameBackwardWithOffset() {
+    fun animatePerFrameBackwardWithOffset() = testScroll {
         assertSpringAnimation(toIndex = 1, fromIndex = 5, fromOffset = 58)
     }
 
     @Test
-    fun animatePerFrameBackwardWithInitialOffset() {
+    fun animatePerFrameBackwardWithInitialOffset() = testScroll {
         assertSpringAnimation(toIndex = 0, toOffset = 20, fromIndex = 8)
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = runBlocking {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, -itemSizePx * 3)
         }
@@ -253,7 +260,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(10)
             state.animateScrollToItem(0, itemSizePx * 3)
@@ -263,14 +270,14 @@
     }
 
     @Test
-    fun canScrollForward() = runBlocking {
+    fun canScrollForward() = testScroll {
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         assertThat(state.canScrollForward).isTrue()
         assertThat(state.canScrollBackward).isFalse()
     }
 
     @Test
-    fun canScrollBackward() = runBlocking {
+    fun canScrollBackward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(itemsCount)
         }
@@ -280,7 +287,7 @@
     }
 
     @Test
-    fun canScrollForwardAndBackward() = runBlocking {
+    fun canScrollForwardAndBackward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(1)
         }
@@ -289,11 +296,32 @@
         assertThat(state.canScrollBackward).isTrue()
     }
 
+    @Test
+    fun animatePerFrameWithSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(toIndex = 8, spacingPx = 10)
+    }
+
+    @Test
+    fun animatePerFrameWithNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(toIndex = 8, spacingPx = -10)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(toIndex = 1, fromIndex = 6, spacingPx = 10)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(toIndex = 1, fromIndex = 6, spacingPx = -10)
+    }
+
     private fun assertSpringAnimation(
         toIndex: Int,
         toOffset: Int = 0,
         fromIndex: Int = 0,
-        fromOffset: Int = 0
+        fromOffset: Int = 0,
+        spacingPx: Int = 0
     ) {
         if (fromIndex != 0 || fromOffset != 0) {
             rule.runOnIdle {
@@ -317,8 +345,9 @@
             Thread.sleep(5)
         }
 
-        val startOffset = (fromIndex * itemSizePx + fromOffset).toFloat()
-        val endOffset = (toIndex * itemSizePx + toOffset).toFloat()
+        val itemSizeWSpacing = itemSizePx + spacingPx
+        val startOffset = (fromIndex * itemSizeWSpacing + fromOffset).toFloat()
+        val endOffset = (toIndex * itemSizeWSpacing + toOffset).toFloat()
         val spec = FloatSpringSpec()
 
         val duration =
@@ -329,8 +358,9 @@
             val nanosTime = TimeUnit.MILLISECONDS.toNanos(i)
             val expectedValue =
                 spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
-            val actualValue =
-                (state.firstVisibleItemIndex * itemSizePx + state.firstVisibleItemScrollOffset)
+            val actualValue = (
+                state.firstVisibleItemIndex * itemSizeWSpacing + state.firstVisibleItemScrollOffset
+                )
             assertWithMessage(
                 "On animation frame at $i index=${state.firstVisibleItemIndex} " +
                     "offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
@@ -346,15 +376,22 @@
     }
 
     @Composable
-    private fun TestContent() {
+    private fun TestContent(spacingDp: Dp) {
         if (vertical) {
-            LazyColumn(Modifier.height(containerSizeDp), state) {
+            LazyColumn(
+                Modifier.height(containerSizeDp),
+                state,
+                verticalArrangement = Arrangement.spacedBy(spacingDp)
+            ) {
                 items(itemsCount) {
                     ItemContent()
                 }
             }
         } else {
-            LazyRow(Modifier.width(containerSizeDp), state) {
+            LazyRow(
+                Modifier.width(containerSizeDp), state,
+                horizontalArrangement = Arrangement.spacedBy(spacingDp)
+            ) {
                 items(itemsCount) {
                     ItemContent()
                 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt
new file mode 100644
index 0000000..984c7d4
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt
@@ -0,0 +1,2181 @@
+/*
+ * 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.staggeredgrid
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.requiredWidthIn
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridAnimateItemPlacementTest(private val config: Config) {
+
+    private val isVertical: Boolean get() = config.isVertical
+    private val reverseLayout: Boolean get() = config.reverseLayout
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    // the numbers should be divisible by 8 to avoid the rounding issues as we run 4 or 8 frames
+    // of the animation.
+    private val itemSize: Float = 40f
+    private var itemSizeDp: Dp = Dp.Infinity
+    private val itemSize2: Float = 24f
+    private var itemSize2Dp: Dp = Dp.Infinity
+    private val itemSize3: Float = 16f
+    private var itemSize3Dp: Dp = Dp.Infinity
+    private val containerSize: Float = itemSize * 5
+    private var containerSizeDp: Dp = Dp.Infinity
+    private val spacing: Float = 8f
+    private var spacingDp: Dp = Dp.Infinity
+    private val itemSizePlusSpacing = itemSize + spacing
+    private var itemSizePlusSpacingDp = Dp.Infinity
+    private lateinit var state: LazyStaggeredGridState
+
+    @Before
+    fun before() {
+        rule.mainClock.autoAdvance = false
+        with(rule.density) {
+            itemSizeDp = itemSize.toDp()
+            itemSize2Dp = itemSize2.toDp()
+            itemSize3Dp = itemSize3.toDp()
+            containerSizeDp = containerSize.toDp()
+            spacingDp = spacing.toDp()
+            itemSizePlusSpacingDp = itemSizePlusSpacing.toDp()
+        }
+    }
+
+    @Test
+    fun reorderTwoItems() {
+        var list by mutableStateOf(listOf(0, 1))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(1, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f + itemSize * fraction),
+                1 to AxisOffset(0f, itemSize - itemSize * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun reorderTwoByTwoItems() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3))
+        rule.setContent {
+            LazyStaggeredGrid(2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(3, 2, 1, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            val increasing = 0 + itemSize * fraction
+            val decreasing = itemSize - itemSize * fraction
+            assertPositions(
+                0 to AxisOffset(increasing, increasing),
+                1 to AxisOffset(decreasing, increasing),
+                2 to AxisOffset(increasing, decreasing),
+                3 to AxisOffset(decreasing, decreasing),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun reorderTwoItems_layoutInfoHasFinalPositions() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3))
+        rule.setContent {
+            LazyStaggeredGrid(2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertLayoutInfoPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(3, 2, 1, 0)
+        }
+
+        onAnimationFrame {
+            // fraction doesn't affect the offsets in layout info
+            assertLayoutInfoPositions(
+                3 to AxisOffset(0f, 0f),
+                2 to AxisOffset(itemSize, 0f),
+                1 to AxisOffset(0f, itemSize),
+                0 to AxisOffset(itemSize, itemSize)
+            )
+        }
+    }
+
+    @Test
+    fun reorderFirstAndLastItems() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize),
+            2 to AxisOffset(0f, itemSize * 2),
+            3 to AxisOffset(0f, itemSize * 3),
+            4 to AxisOffset(0f, itemSize * 4)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(4, 1, 2, 3, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f + itemSize * 4 * fraction),
+                1 to AxisOffset(0f, itemSize),
+                2 to AxisOffset(0f, itemSize * 2),
+                3 to AxisOffset(0f, itemSize * 3),
+                4 to AxisOffset(0f, itemSize * 4 - itemSize * 4 * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveFirstItemToEndCausingAllItemsToAnimate() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+        rule.setContent {
+            LazyStaggeredGrid(2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize),
+            4 to AxisOffset(0f, itemSize * 2),
+            5 to AxisOffset(itemSize, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(1, 2, 3, 4, 5, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            val increasingX = 0 + itemSize * fraction
+            val decreasingX = itemSize - itemSize * fraction
+            assertPositions(
+                0 to AxisOffset(increasingX, 0f + itemSize * 2 * fraction),
+                1 to AxisOffset(decreasingX, 0f),
+                2 to AxisOffset(increasingX, itemSize - itemSize * fraction),
+                3 to AxisOffset(decreasingX, itemSize),
+                4 to AxisOffset(increasingX, itemSize * 2 - itemSize * fraction),
+                5 to AxisOffset(decreasingX, itemSize * 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun itemSizeChangeAnimatesNextItems() {
+        var size by mutableStateOf(itemSizeDp)
+        rule.setContent {
+            LazyStaggeredGrid(1, minSize = itemSizeDp * 5, maxSize = itemSizeDp * 5) {
+                items(listOf(0, 1, 2, 3), key = { it }) {
+                    Item(it, size = if (it == 1) size else itemSizeDp)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            size = itemSizeDp * 2
+        }
+        rule.mainClock.advanceTimeByFrame()
+
+        rule.onNodeWithTag("1")
+            .assertMainAxisSizeIsEqualTo(size)
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f),
+                1 to AxisOffset(0f, itemSize),
+                2 to AxisOffset(0f, itemSize * 2 + itemSize * fraction),
+                3 to AxisOffset(0f, itemSize * 3 + itemSize * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun onlyItemsWithModifierAnimates() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it, animSpec = if (it == 1 || it == 3) AnimSpec else null)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(1, 2, 3, 4, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, itemSize * 4),
+                1 to AxisOffset(0f, itemSize - itemSize * fraction),
+                2 to AxisOffset(0f, itemSize),
+                3 to AxisOffset(0f, itemSize * 3 - itemSize * fraction),
+                4 to AxisOffset(0f, itemSize * 3),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun animationsWithDifferentDurations() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    val duration = if (it == 1 || it == 3) Duration * 2 else Duration
+                    Item(it, animSpec = tween(duration.toInt(), easing = LinearEasing))
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(1, 2, 3, 4, 0)
+        }
+
+        onAnimationFrame(duration = Duration * 2) { fraction ->
+            val shorterAnimFraction = (fraction * 2).coerceAtMost(1f)
+            assertPositions(
+                0 to AxisOffset(0f, 0 + itemSize * 4 * shorterAnimFraction),
+                1 to AxisOffset(0f, itemSize - itemSize * fraction),
+                2 to AxisOffset(0f, itemSize * 2 - itemSize * shorterAnimFraction),
+                3 to AxisOffset(0f, itemSize * 3 - itemSize * fraction),
+                4 to AxisOffset(0f, itemSize * 4 - itemSize * shorterAnimFraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun multipleChildrenPerItem() {
+        var list by mutableStateOf(listOf(0, 2))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it)
+                    Item(it + 1)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(0f, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(2, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0 + itemSize * fraction),
+                1 to AxisOffset(0f, 0 + itemSize * fraction),
+                2 to AxisOffset(0f, itemSize - itemSize * fraction),
+                3 to AxisOffset(0f, itemSize - itemSize * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun multipleChildrenPerItemSomeDoNotAnimate() {
+        var list by mutableStateOf(listOf(0, 2))
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it)
+                    Item(it + 1, animSpec = null)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(2, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0 + itemSize * fraction),
+                1 to AxisOffset(0f, itemSize),
+                2 to AxisOffset(0f, itemSize - itemSize * fraction),
+                3 to AxisOffset(0f, 0f),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun animateSpacingChange() {
+        var currentSpacing by mutableStateOf(0.dp)
+        rule.setContent {
+            LazyStaggeredGrid(
+                1,
+                spacing = currentSpacing
+            ) {
+                items(listOf(0, 1), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize),
+        )
+
+        rule.runOnUiThread {
+            currentSpacing = spacingDp
+        }
+        rule.mainClock.advanceTimeByFrame()
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f),
+                1 to AxisOffset(0f, itemSize + spacing * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheBottomOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+        val gridSize = itemSize * 3
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize),
+            4 to AxisOffset(0f, itemSize * 2),
+            5 to AxisOffset(itemSize, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 1 moves to and item 8 moves from `gridSize`, right after the end edge
+            val item1Offset = AxisOffset(itemSize, 0 + gridSize * fraction)
+            val item8Offset =
+                AxisOffset(itemSize, gridSize - gridSize * fraction)
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                if (item1Offset.mainAxis < itemSize * 3) {
+                    add(1 to item1Offset)
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                add(2 to AxisOffset(0f, itemSize))
+                add(3 to AxisOffset(itemSize, itemSize))
+                add(4 to AxisOffset(0f, itemSize * 2))
+                add(5 to AxisOffset(itemSize, itemSize * 2))
+                if (item8Offset.mainAxis < itemSize * 3) {
+                    add(8 to item8Offset)
+                } else {
+                    rule.onNodeWithTag("8").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheTopOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = itemSizeDp * 3, startIndex = 6) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            6 to AxisOffset(0f, 0f),
+            7 to AxisOffset(itemSize, 0f),
+            8 to AxisOffset(0f, itemSize),
+            9 to AxisOffset(itemSize, itemSize),
+            10 to AxisOffset(0f, itemSize * 2),
+            11 to AxisOffset(itemSize, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 1 moves from and item 8 moves to `0 - itemSize`, right before the start edge
+            val item8Offset = AxisOffset(0f, itemSize - itemSize * 2 * fraction)
+            val item1Offset = AxisOffset(0f, -itemSize + itemSize * 2 * fraction)
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (item1Offset.mainAxis > -itemSize) {
+                    add(1 to item1Offset)
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                add(6 to AxisOffset(0f, 0f))
+                add(7 to AxisOffset(itemSize, 0f))
+                if (item8Offset.mainAxis > -itemSize) {
+                    add(8 to item8Offset)
+                } else {
+                    rule.onNodeWithTag("8").assertIsNotDisplayed()
+                }
+                add(9 to AxisOffset(itemSize, itemSize))
+                add(10 to AxisOffset(0f, itemSize * 2))
+                add(11 to AxisOffset(itemSize, itemSize * 2))
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveFirstItemToEndCausingAllItemsToAnimate_withSpacing() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7))
+        rule.setContent {
+            LazyStaggeredGrid(2, spacing = spacingDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(1, 2, 3, 4, 5, 6, 7, 0)
+        }
+
+        onAnimationFrame { fraction ->
+            val increasingX = fraction * itemSize
+            val decreasingX = itemSize - itemSize * fraction
+            assertPositions(
+                0 to AxisOffset(increasingX, itemSizePlusSpacing * 3 * fraction),
+                1 to AxisOffset(decreasingX, 0f),
+                2 to AxisOffset(
+                    increasingX,
+                    itemSizePlusSpacing - itemSizePlusSpacing * fraction
+                ),
+                3 to AxisOffset(decreasingX, itemSizePlusSpacing),
+                4 to AxisOffset(
+                    increasingX,
+                    itemSizePlusSpacing * 2 - itemSizePlusSpacing * fraction
+                ),
+                5 to AxisOffset(decreasingX, itemSizePlusSpacing * 2),
+                6 to AxisOffset(
+                    increasingX,
+                    itemSizePlusSpacing * 3 - itemSizePlusSpacing * fraction
+                ),
+                7 to AxisOffset(decreasingX, itemSizePlusSpacing * 3),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheBottomOutsideOfBounds_withSpacing() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+        val gridSize = itemSize * 3 + spacing * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(
+                2,
+                maxSize = gridSizeDp,
+                spacing = spacingDp
+            ) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSizePlusSpacing),
+            3 to AxisOffset(itemSize, itemSizePlusSpacing),
+            4 to AxisOffset(0f, itemSizePlusSpacing * 2),
+            5 to AxisOffset(itemSize, itemSizePlusSpacing * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 1 moves to and item 8 moves from `gridSize`, right after the end edge
+            val item1Offset = AxisOffset(itemSize, gridSize * fraction)
+            val item8Offset = AxisOffset(itemSize, gridSize - gridSize * fraction)
+            val screenSize = itemSize * 3 + spacing * 2
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                if (item1Offset.mainAxis < screenSize) {
+                    add(1 to item1Offset)
+                }
+                add(2 to AxisOffset(0f, itemSizePlusSpacing))
+                add(3 to AxisOffset(itemSize, itemSizePlusSpacing))
+                add(4 to AxisOffset(0f, itemSizePlusSpacing * 2))
+                add(5 to AxisOffset(itemSize, itemSizePlusSpacing * 2))
+                if (item8Offset.mainAxis < screenSize) {
+                    add(8 to item8Offset)
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheTopOutsideOfBounds_withSpacing() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+        rule.setContent {
+            LazyStaggeredGrid(
+                2,
+                maxSize = itemSizeDp * 3 + spacingDp * 2,
+                spacing = spacingDp,
+                startIndex = 4
+            ) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            4 to AxisOffset(0f, 0f),
+            5 to AxisOffset(itemSize, 0f),
+            6 to AxisOffset(0f, itemSizePlusSpacing),
+            7 to AxisOffset(itemSize, itemSizePlusSpacing),
+            8 to AxisOffset(0f, itemSizePlusSpacing * 2),
+            9 to AxisOffset(itemSize, itemSizePlusSpacing * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 8 moves to and item 1 moves from `-itemSize`, right before the start edge
+            val item1Offset = AxisOffset(
+                0f,
+                -itemSize + (itemSize + itemSizePlusSpacing * 2) * fraction
+            )
+            val item8Offset = AxisOffset(
+                0f,
+                itemSizePlusSpacing * 2 -
+                    (itemSize + itemSizePlusSpacing * 2) * fraction
+            )
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (item1Offset.mainAxis > -itemSize) {
+                    add(1 to item1Offset)
+                }
+                add(4 to AxisOffset(0f, 0f))
+                add(5 to AxisOffset(itemSize, 0f))
+                add(6 to AxisOffset(0f, itemSizePlusSpacing))
+                add(7 to AxisOffset(itemSize, itemSizePlusSpacing))
+                if (item8Offset.mainAxis > -itemSize) {
+                    add(8 to item8Offset)
+                }
+                add(9 to AxisOffset(itemSize, itemSizePlusSpacing * 2))
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheTopOutsideOfBounds_differentSizes() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = itemSize2Dp * 2, startIndex = 6) {
+                items(list, key = { it }) {
+                    val height = when (it) {
+                        2 -> itemSize3Dp
+                        6, 9 -> itemSize2Dp
+                        7 -> itemSize3Dp
+                        8 -> itemSizeDp
+                        else -> itemSizeDp
+                    }
+                    Item(it, size = height)
+                }
+            }
+        }
+
+        val item2Size = itemSize3
+        val item6Size = itemSize2
+        val item7Size = itemSize3
+        val item8Size = itemSize
+        assertPositions(
+            6 to AxisOffset(0f, 0f),
+            7 to AxisOffset(itemSize, 0f),
+            8 to AxisOffset(itemSize, item7Size),
+            9 to AxisOffset(0f, item6Size)
+        )
+
+        rule.runOnUiThread {
+            // swap 8 and 2
+            list = listOf(0, 1, 8, 3, 4, 5, 6, 7, 2, 9, 10, 11)
+        }
+
+        onAnimationFrame { fraction ->
+            rule.onNodeWithTag("3").assertDoesNotExist()
+            rule.onNodeWithTag("4").assertDoesNotExist()
+            rule.onNodeWithTag("5").assertDoesNotExist()
+            // item 2 moves from and item 8 moves to `0 - item size`, right before the start edge
+            val startItem2Offset = -item2Size
+            val item2Offset =
+                startItem2Offset + (item7Size - startItem2Offset) * fraction
+            val endItem8Offset = -item8Size
+            val item8Offset = item7Size - (item7Size - endItem8Offset) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (item8Offset > -item8Size) {
+                    add(8 to AxisOffset(itemSize, item8Offset))
+                } else {
+                    rule.onNodeWithTag("8").assertIsNotDisplayed()
+                }
+                add(6 to AxisOffset(0f, 0f))
+                add(7 to AxisOffset(itemSize, 0f))
+                if (item2Offset > -item2Size) {
+                    add(2 to AxisOffset(itemSize, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+                add(9 to AxisOffset(0f, item6Size))
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToTheBottomOutsideOfBounds_differentSizes() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8))
+        val gridSize = itemSize2 * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    val height = when (it) {
+                        0, 3 -> itemSize2Dp
+                        1 -> itemSize3Dp
+                        2 -> itemSizeDp
+                        8 -> itemSize3Dp
+                        else -> itemSizeDp
+                    }
+                    Item(it, size = height)
+                }
+            }
+        }
+
+        val item0Size = itemSize2
+        val item1Size = itemSize3
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(itemSize, item1Size),
+            3 to AxisOffset(0f, item0Size)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 1, 8, 3, 4, 5, 6, 7, 2, 9, 10, 11)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 8 moves from and item 2 moves to `gridSize`, right after the end edge
+            val startItem8Offset = gridSize
+            val endItem2Offset = gridSize
+            val item2Offset =
+                item1Size + (endItem2Offset - item1Size) * fraction
+            val item8Offset =
+                startItem8Offset - (startItem8Offset - item1Size) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                add(1 to AxisOffset(itemSize, 0f))
+                if (item8Offset < gridSize) {
+                    add(8 to AxisOffset(itemSize, item8Offset))
+                } else {
+                    rule.onNodeWithTag("8").assertIsNotDisplayed()
+                }
+                add(3 to AxisOffset(0f, item0Size))
+                if (item2Offset < gridSize) {
+                    add(2 to AxisOffset(itemSize, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveItemToEndCausingNextItemsToAnimate_withContentPadding() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val rawStartPadding = 8f
+        val rawEndPadding = 12f
+        val (startPaddingDp, endPaddingDp) = with(rule.density) {
+            rawStartPadding.toDp() to rawEndPadding.toDp()
+        }
+        rule.setContent {
+            LazyStaggeredGrid(1, startPadding = startPaddingDp, endPadding = endPaddingDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        val startPadding = if (reverseLayout) rawEndPadding else rawStartPadding
+        assertPositions(
+            0 to AxisOffset(0f, startPadding),
+            1 to AxisOffset(0f, startPadding + itemSize),
+            2 to AxisOffset(0f, startPadding + itemSize * 2),
+            3 to AxisOffset(0f, startPadding + itemSize * 3),
+            4 to AxisOffset(0f, startPadding + itemSize * 4),
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 2, 3, 4, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, startPadding),
+                1 to AxisOffset(
+                    0f,
+                    startPadding + itemSize + itemSize * 3 * fraction
+                ),
+                2 to AxisOffset(
+                    0f,
+                    startPadding + itemSize * 2 - itemSize * fraction
+                ),
+                3 to AxisOffset(
+                    0f,
+                    startPadding + itemSize * 3 - itemSize * fraction
+                ),
+                4 to AxisOffset(
+                    0f,
+                    startPadding + itemSize * 4 - itemSize * fraction
+                ),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun reorderFirstAndLastItems_noNewLayoutInfoProduced() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+
+        var measurePasses = 0
+        rule.setContent {
+            LazyStaggeredGrid(1) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+            LaunchedEffect(Unit) {
+                snapshotFlow { state.layoutInfo }
+                    .collect {
+                        measurePasses++
+                    }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(4, 1, 2, 3, 0)
+        }
+
+        var startMeasurePasses = Int.MIN_VALUE
+        onAnimationFrame { fraction ->
+            if (fraction == 0f) {
+                startMeasurePasses = measurePasses
+            }
+        }
+        rule.mainClock.advanceTimeByFrame()
+        // new layoutInfo is produced on every remeasure of Lazy lists.
+        // but we want to avoid remeasuring and only do relayout on each animation frame.
+        // two extra measures are possible as we switch inProgress flag.
+        assertThat(measurePasses).isAtMost(startMeasurePasses + 2)
+    }
+
+    @Test
+    fun noAnimationWhenScrolledToOtherPosition() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollToItem(0, (itemSize / 2).roundToInt())
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(0f, itemSize / 2),
+                2 to AxisOffset(0f, itemSize * 3 / 2),
+                3 to AxisOffset(0f, itemSize * 5 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollForwardBySmallOffset() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize / 2f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(0f, itemSize / 2),
+                2 to AxisOffset(0f, itemSize * 3 / 2),
+                3 to AxisOffset(0f, itemSize * 5 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollBackwardBySmallOffset() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 2) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(-itemSize / 2f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                1 to AxisOffset(0f, -itemSize / 2),
+                2 to AxisOffset(0f, itemSize / 2),
+                3 to AxisOffset(0f, itemSize * 3 / 2),
+                4 to AxisOffset(0f, itemSize * 5 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollForwardByLargeOffset() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                2 to AxisOffset(0f, -itemSize / 2),
+                3 to AxisOffset(0f, itemSize / 2),
+                4 to AxisOffset(0f, itemSize * 3 / 2),
+                5 to AxisOffset(0f, itemSize * 5 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollBackwardByLargeOffset() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(-itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(0f, itemSize / 2),
+                2 to AxisOffset(0f, itemSize * 3 / 2),
+                3 to AxisOffset(0f, itemSize * 5 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollForwardByLargeOffset_differentSizes() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it, size = if (it % 2 == 0) itemSizeDp else itemSize2Dp)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize + itemSize2 + itemSize / 2f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                2 to AxisOffset(0f, -itemSize / 2),
+                3 to AxisOffset(0f, itemSize / 2),
+                4 to AxisOffset(0f, itemSize2 + itemSize / 2),
+                5 to AxisOffset(0f, itemSize2 + itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollBackwardByLargeOffset_differentSizes() {
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 3) {
+                items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+                    Item(it, size = if (it % 2 == 0) itemSizeDp else itemSize2Dp)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(-(itemSize + itemSize2 + itemSize / 2f))
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(0f, itemSize / 2),
+                2 to AxisOffset(0f, itemSize2 + itemSize / 2),
+                3 to AxisOffset(0f, itemSize2 + itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollForwardByLargeOffset_multipleCells() {
+        rule.setContent {
+            LazyStaggeredGrid(3, maxSize = itemSizeDp * 2) {
+                items(List(20) { it }, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(itemSize * 2, 0f),
+            3 to AxisOffset(0f, itemSize),
+            4 to AxisOffset(itemSize, itemSize),
+            5 to AxisOffset(itemSize * 2, itemSize)
+        )
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                6 to AxisOffset(0f, -itemSize / 2),
+                7 to AxisOffset(itemSize, -itemSize / 2),
+                8 to AxisOffset(itemSize * 2, -itemSize / 2),
+                9 to AxisOffset(0f, itemSize / 2),
+                10 to AxisOffset(itemSize, itemSize / 2),
+                11 to AxisOffset(itemSize * 2, itemSize / 2),
+                12 to AxisOffset(0f, itemSize * 3 / 2),
+                13 to AxisOffset(itemSize, itemSize * 3 / 2),
+                14 to AxisOffset(itemSize * 2, itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollBackwardByLargeOffset_multipleCells() {
+        rule.setContent {
+            LazyStaggeredGrid(3, maxSize = itemSizeDp * 2, startIndex = 9) {
+                items(List(20) { it }, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            9 to AxisOffset(0f, 0f),
+            10 to AxisOffset(itemSize, 0f),
+            11 to AxisOffset(itemSize * 2, 0f),
+            12 to AxisOffset(0f, itemSize),
+            13 to AxisOffset(itemSize, itemSize),
+            14 to AxisOffset(itemSize * 2, itemSize)
+        )
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(-itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(itemSize, -itemSize / 2),
+                2 to AxisOffset(itemSize * 2, -itemSize / 2),
+                3 to AxisOffset(0f, itemSize / 2),
+                4 to AxisOffset(itemSize, itemSize / 2),
+                5 to AxisOffset(itemSize * 2, itemSize / 2),
+                6 to AxisOffset(0f, itemSize * 3 / 2),
+                7 to AxisOffset(itemSize, itemSize * 3 / 2),
+                8 to AxisOffset(itemSize * 2, itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollForwardByLargeOffset_differentSpans() {
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = itemSizeDp * 2) {
+                items(
+                    List(10) { it },
+                    key = { it },
+                    span = {
+                        if (it == 6) {
+                            StaggeredGridItemSpan.FullLine
+                        } else {
+                            StaggeredGridItemSpan.SingleLane
+                        }
+                    }
+                ) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize),
+        )
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                4 to AxisOffset(0f, -itemSize / 2),
+                5 to AxisOffset(itemSize, -itemSize / 2),
+                6 to AxisOffset(0f, itemSize / 2), // 3 spans
+                7 to AxisOffset(0f, itemSize * 3 / 2),
+                8 to AxisOffset(itemSize, itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenScrollBackwardByLargeOffset_differentSpans() {
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = itemSizeDp * 2) {
+                items(
+                    List(10) { it },
+                    key = { it },
+                    span = {
+                        if (it == 2) {
+                            StaggeredGridItemSpan.FullLine
+                        } else {
+                            StaggeredGridItemSpan.SingleLane
+                        }
+                    }
+                ) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(itemSize * 3f)
+            }
+        }
+
+        assertPositions(
+            5 to AxisOffset(0f, 0f),
+            6 to AxisOffset(itemSize, 0f),
+            7 to AxisOffset(0f, itemSize),
+            8 to AxisOffset(itemSize, itemSize),
+        )
+
+        rule.runOnUiThread {
+            runBlocking {
+                state.scrollBy(-itemSize * 2.5f)
+            }
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, -itemSize / 2),
+                1 to AxisOffset(itemSize, -itemSize / 2),
+                2 to AxisOffset(0f, itemSize / 2), // 3 spans
+                3 to AxisOffset(0f, itemSize * 3 / 2),
+                4 to AxisOffset(itemSize, itemSize * 3 / 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun animatingItemWithPreviousIndexLargerThanTheNewItemCount() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7))
+        val gridSize = itemSize * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertLayoutInfoPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize),
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 6)
+        }
+
+        onAnimationFrame { fraction ->
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                val item6MainAxis = gridSize - (gridSize - itemSize) * fraction
+                if (item6MainAxis < gridSize) {
+                    add(6 to AxisOffset(0f, item6MainAxis))
+                } else {
+                    rule.onNodeWithTag("6").assertIsNotDisplayed()
+                }
+            }
+
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun animatingItemsWithPreviousIndexLargerThanTheNewItemCount_differentSpans() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6))
+        val gridSize = itemSize * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+                items(list, key = { it }, span = {
+                    if (it == 6) {
+                        StaggeredGridItemSpan.FullLine
+                    } else {
+                        StaggeredGridItemSpan.SingleLane
+                    }
+                }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertLayoutInfoPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 4, 6)
+        }
+
+        onAnimationFrame { fraction ->
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                val item4MainAxis = gridSize - gridSize * fraction
+                if (item4MainAxis < gridSize) {
+                    add(
+                        4 to AxisOffset(itemSize, item4MainAxis)
+                    )
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                }
+                val item6MainAxis = gridSize - (gridSize - itemSize) * fraction
+                if (item6MainAxis < gridSize) {
+                    add(
+                        6 to AxisOffset(0f, item6MainAxis)
+                    )
+                } else {
+                    rule.onNodeWithTag("6").assertIsNotDisplayed()
+                }
+            }
+
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun itemWithSpecsIsMovingOut() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3))
+        val gridSize = itemSize * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it, animSpec = if (it == 1) AnimSpec else null)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(0, 2, 3, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 1 moves to `gridSize`
+            val item1Offset = itemSize + (gridSize - itemSize) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                if (item1Offset < gridSize) {
+                    add(1 to AxisOffset(0f, item1Offset))
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheTopOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3f, startIndex = 3) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            3 to AxisOffset(0f, 0f),
+            4 to AxisOffset(0f, itemSize),
+            5 to AxisOffset(0f, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 4, 5, 3, 1, 2)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 2 moves from and item 5 moves to `-itemSize`, right before the start edge
+            val item2Offset = -itemSize + itemSize * 3 * fraction
+            val item5Offset = itemSize * 2 - itemSize * 3 * fraction
+            // item 1 moves from and item 4 moves to `-itemSize * 2`, right before item 2
+            val item1Offset = -itemSize * 2 + itemSize * 3 * fraction
+            val item4Offset = itemSize - itemSize * 3 * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (item1Offset > -itemSize) {
+                    add(1 to AxisOffset(0f, item1Offset))
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                if (item2Offset > -itemSize) {
+                    add(2 to AxisOffset(0f, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+                add(3 to AxisOffset(0f, 0f))
+                if (item4Offset > -itemSize) {
+                    add(4 to AxisOffset(0f, item4Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                }
+                if (item5Offset > -itemSize) {
+                    add(5 to AxisOffset(0f, item5Offset))
+                } else {
+                    rule.onNodeWithTag("5").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheTopOutsideOfBounds_withReordering() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 3f, startIndex = 3) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            3 to AxisOffset(0f, 0f),
+            4 to AxisOffset(0f, itemSize),
+            5 to AxisOffset(0f, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 5, 4, 3, 2, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 2 moves from and item 4 moves to `-itemSize`, right before the start edge
+            val item2Offset = -itemSize + itemSize * 2 * fraction
+            val item4Offset = itemSize - itemSize * 2 * fraction
+            // item 1 moves from and item 5 moves to `-itemSize * 2`, right before item 2
+            val item1Offset = -itemSize * 2 + itemSize * 4 * fraction
+            val item5Offset = itemSize * 2 - itemSize * 4 * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (item1Offset > -itemSize) {
+                    add(1 to AxisOffset(0f, item1Offset))
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                if (item2Offset > -itemSize) {
+                    add(2 to AxisOffset(0f, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+                add(3 to AxisOffset(0f, 0f))
+                if (item4Offset > -itemSize) {
+                    add(4 to AxisOffset(0f, item4Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                }
+                if (item5Offset > -itemSize) {
+                    add(5 to AxisOffset(0f, item5Offset))
+                } else {
+                    rule.onNodeWithTag("5").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheTopOutsideOfBounds_itemsOfDifferentLanes() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = itemSizeDp * 2f, startIndex = 2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            2 to AxisOffset(0f, 0f),
+            3 to AxisOffset(itemSize, 0f),
+            4 to AxisOffset(0f, itemSize),
+            5 to AxisOffset(itemSize, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(4, 5, 2, 3, 0, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            // items 0 and 2 moves from and items 4 and 5 moves to `-itemSize`,
+            // right before the start edge
+            val items0and1Offset = -itemSize + itemSize * 2 * fraction
+            val items4and5Offset = itemSize - itemSize * 2 * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                if (items0and1Offset > -itemSize) {
+                    add(0 to AxisOffset(0f, items0and1Offset))
+                    add(1 to AxisOffset(itemSize, items0and1Offset))
+                } else {
+                    rule.onNodeWithTag("0").assertIsNotDisplayed()
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                add(2 to AxisOffset(0f, 0f))
+                add(3 to AxisOffset(itemSize, 0f))
+                if (items4and5Offset > -itemSize) {
+                    add(4 to AxisOffset(0f, items4and5Offset))
+                    add(5 to AxisOffset(itemSize, items4and5Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                    rule.onNodeWithTag("5").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheBottomOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val gridSize = itemSize * 3
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize),
+            2 to AxisOffset(0f, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 3, 4, 1, 2)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 1 moves to and item 3 moves from `gridSize`, right after the end edge
+            val item1Offset = itemSize + (gridSize - itemSize) * fraction
+            val item3Offset = gridSize - (gridSize - itemSize) * fraction
+            // item 2 moves to and item 4 moves from `gridSize + itemSize`, right after item 4
+            val item2Offset = itemSize * 2 + (gridSize - itemSize) * fraction
+            val item4Offset = gridSize + itemSize - (gridSize - itemSize) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                if (item1Offset < gridSize) {
+                    add(1 to AxisOffset(0f, item1Offset))
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                if (item2Offset < gridSize) {
+                    add(2 to AxisOffset(0f, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+                if (item3Offset < gridSize) {
+                    add(3 to AxisOffset(0f, item3Offset))
+                } else {
+                    rule.onNodeWithTag("3").assertIsNotDisplayed()
+                }
+                if (item4Offset < gridSize) {
+                    add(4 to AxisOffset(0f, item4Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheBottomOutsideOfBounds_withReordering() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val gridSize = itemSize * 3
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(0f, itemSize),
+            2 to AxisOffset(0f, itemSize * 2)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 4, 3, 2, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            // item 2 moves to and item 3 moves from `gridSize`, right after the end edge
+            val item2Offset = itemSize * 2 + (gridSize - itemSize * 2) * fraction
+            val item3Offset = gridSize - (gridSize - itemSize * 2) * fraction
+            // item 1 moves to and item 4 moves from `gridSize + itemSize`, right after item 4
+            val item1Offset = itemSize + (gridSize + itemSize - itemSize) * fraction
+            val item4Offset =
+                gridSize + itemSize - (gridSize + itemSize - itemSize) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                if (item1Offset < gridSize) {
+                    add(1 to AxisOffset(0f, item1Offset))
+                } else {
+                    rule.onNodeWithTag("1").assertIsNotDisplayed()
+                }
+                if (item2Offset < gridSize) {
+                    add(2 to AxisOffset(0f, item2Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                }
+                if (item3Offset < gridSize) {
+                    add(3 to AxisOffset(0f, item3Offset))
+                } else {
+                    rule.onNodeWithTag("3").assertIsNotDisplayed()
+                }
+                if (item4Offset < gridSize) {
+                    add(4 to AxisOffset(0f, item4Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun moveTwoItemsToTheBottomOutsideOfBounds_itemsOfDifferentLanes() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+        val gridSize = itemSize * 2
+        val gridSizeDp = with(rule.density) { gridSize.toDp() }
+        rule.setContent {
+            LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertPositions(
+            0 to AxisOffset(0f, 0f),
+            1 to AxisOffset(itemSize, 0f),
+            2 to AxisOffset(0f, itemSize),
+            3 to AxisOffset(itemSize, itemSize)
+        )
+
+        rule.runOnUiThread {
+            list = listOf(0, 1, 4, 5, 2, 3)
+        }
+
+        onAnimationFrame { fraction ->
+            // items 4 and 5 moves from and items 2 and 3 moves to `gridSize`,
+            // right before the start edge
+            val items4and5Offset = gridSize - (gridSize - itemSize) * fraction
+            val items2and3Offset = itemSize + (gridSize - itemSize) * fraction
+            val expected = mutableListOf<Pair<Any, Offset>>().apply {
+                add(0 to AxisOffset(0f, 0f))
+                add(1 to AxisOffset(itemSize, 0f))
+                if (items2and3Offset < gridSize) {
+                    add(2 to AxisOffset(0f, items2and3Offset))
+                    add(3 to AxisOffset(itemSize, items2and3Offset))
+                } else {
+                    rule.onNodeWithTag("2").assertIsNotDisplayed()
+                    rule.onNodeWithTag("3").assertIsNotDisplayed()
+                }
+                if (items4and5Offset < gridSize) {
+                    add(4 to AxisOffset(0f, items4and5Offset))
+                    add(5 to AxisOffset(itemSize, items4and5Offset))
+                } else {
+                    rule.onNodeWithTag("4").assertIsNotDisplayed()
+                    rule.onNodeWithTag("5").assertIsNotDisplayed()
+                }
+            }
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun noAnimationWhenParentSizeShrinks() {
+        var size by mutableStateOf(itemSizeDp * 3)
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = size) {
+                items(listOf(0, 1, 2), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            size = itemSizeDp * 2
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f),
+                1 to AxisOffset(0f, itemSize),
+                fraction = fraction
+            )
+            rule.onNodeWithTag("2").assertIsNotDisplayed()
+        }
+    }
+
+    @Test
+    fun noAnimationWhenParentSizeExpands() {
+        var size by mutableStateOf(itemSizeDp * 2)
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = size) {
+                items(listOf(0, 1, 2), key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            size = itemSizeDp * 3
+        }
+
+        onAnimationFrame { fraction ->
+            assertPositions(
+                0 to AxisOffset(0f, 0f),
+                1 to AxisOffset(0f, itemSize),
+                2 to AxisOffset(0f, itemSize * 2),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun scrollIsAffectingItemsMovingWithinViewport() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3))
+        val scrollDelta = spacing
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = itemSizeDp * 2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(0, 2, 1, 3)
+        }
+
+        onAnimationFrame { fraction ->
+            if (fraction == 0f) {
+                assertPositions(
+                    0 to AxisOffset(0f, 0f),
+                    1 to AxisOffset(0f, itemSize),
+                    2 to AxisOffset(0f, itemSize * 2),
+                    fraction = fraction
+                )
+                rule.runOnUiThread {
+                    runBlocking { state.scrollBy(scrollDelta) }
+                }
+            }
+            assertPositions(
+                0 to AxisOffset(0f, -scrollDelta),
+                1 to AxisOffset(0f, itemSize - scrollDelta + itemSize * fraction),
+                2 to AxisOffset(0f, itemSize * 2 - scrollDelta - itemSize * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun scrollIsNotAffectingItemMovingToTheBottomOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val scrollDelta = spacing
+        val containerSizeDp = itemSizeDp * 2
+        val containerSize = itemSize * 2
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = containerSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(0, 4, 2, 3, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            if (fraction == 0f) {
+                assertPositions(
+                    0 to AxisOffset(0f, 0f),
+                    1 to AxisOffset(0f, itemSize),
+                    fraction = fraction
+                )
+                rule.runOnUiThread {
+                    runBlocking { state.scrollBy(scrollDelta) }
+                }
+            }
+            assertPositions(
+                0 to AxisOffset(0f, -scrollDelta),
+                1 to AxisOffset(0f, itemSize + (containerSize - itemSize) * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun scrollIsNotAffectingItemMovingToTheTopOutsideOfBounds() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val scrollDelta = -spacing
+        val containerSizeDp = itemSizeDp * 2
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = containerSizeDp, startIndex = 2) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(3, 0, 1, 2, 4)
+        }
+
+        onAnimationFrame { fraction ->
+            if (fraction == 0f) {
+                assertPositions(
+                    2 to AxisOffset(0f, 0f),
+                    3 to AxisOffset(0f, itemSize),
+                    fraction = fraction
+                )
+                rule.runOnUiThread {
+                    runBlocking { state.scrollBy(scrollDelta) }
+                }
+            }
+            assertPositions(
+                2 to AxisOffset(0f, -scrollDelta),
+                3 to AxisOffset(0f, itemSize - (itemSize * 2 * fraction)),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun afterScrollingEnoughToReachNewPositionScrollDeltasStartAffectingPosition() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        val containerSizeDp = itemSizeDp * 2
+        val scrollDelta = spacing
+        rule.setContent {
+            LazyStaggeredGrid(1, maxSize = containerSizeDp) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        rule.runOnUiThread {
+            list = listOf(0, 4, 2, 3, 1)
+        }
+
+        onAnimationFrame { fraction ->
+            if (fraction == 0f) {
+                assertPositions(
+                    0 to AxisOffset(0f, 0f),
+                    1 to AxisOffset(0f, itemSize),
+                    fraction = fraction
+                )
+                rule.runOnUiThread {
+                    runBlocking { state.scrollBy(itemSize * 2) }
+                }
+                assertPositions(
+                    2 to AxisOffset(0f, 0f),
+                    3 to AxisOffset(0f, itemSize),
+                    // after the first scroll the new position of item 1 is still not reached
+                    // so the target didn't change, we still aim to end right after the bounds
+                    1 to AxisOffset(0f, itemSize),
+                    fraction = fraction
+                )
+                rule.runOnUiThread {
+                    runBlocking { state.scrollBy(scrollDelta) }
+                }
+                assertPositions(
+                    2 to AxisOffset(0f, 0f - scrollDelta),
+                    3 to AxisOffset(0f, itemSize - scrollDelta),
+                    // after the second scroll the item 1 is visible, so we know its new target
+                    // position. the animation is now targeting the real end position and now
+                    // we are reacting on the scroll deltas
+                    1 to AxisOffset(0f, itemSize - scrollDelta),
+                    fraction = fraction
+                )
+            }
+            assertPositions(
+                2 to AxisOffset(0f, -scrollDelta),
+                3 to AxisOffset(0f, itemSize - scrollDelta),
+                1 to AxisOffset(0f, itemSize - scrollDelta + itemSize * fraction),
+                fraction = fraction
+            )
+        }
+    }
+
+    private fun AxisOffset(crossAxis: Float, mainAxis: Float) =
+        if (isVertical) Offset(crossAxis, mainAxis) else Offset(mainAxis, crossAxis)
+
+    private val Offset.mainAxis: Float get() = if (isVertical) y else x
+
+    private fun assertPositions(
+        vararg expected: Pair<Any, Offset>,
+        crossAxis: List<Pair<Any, Float>>? = null,
+        fraction: Float? = null,
+        autoReverse: Boolean = reverseLayout
+    ) {
+        val roundedExpected = expected.map { it.first to it.second.round() }
+        val actualBounds = rule.onAllNodes(NodesWithTagMatcher)
+            .fetchSemanticsNodes()
+            .associateBy(
+                keySelector = { it.config[SemanticsProperties.TestTag] },
+                valueTransform = { IntRect(it.positionInRoot.round(), it.size) }
+            )
+        val actualPositions = expected.map {
+            it.first to actualBounds.getValue(it.first.toString()).topLeft
+        }
+        val subject = if (fraction == null) {
+            assertThat(actualPositions)
+        } else {
+            Truth.assertWithMessage("Fraction=$fraction").that(actualPositions)
+        }
+        subject.isEqualTo(
+            roundedExpected.let { list ->
+                if (!autoReverse) {
+                    list
+                } else {
+                    val containerSize = actualBounds.getValue(ContainerTag).size
+                    list.map {
+                        val itemSize = actualBounds.getValue(it.first.toString()).size
+                        it.first to
+                            IntOffset(
+                                if (isVertical) {
+                                    it.second.x
+                                } else {
+                                    containerSize.width - itemSize.width - it.second.x
+                                },
+                                if (!isVertical) {
+                                    it.second.y
+                                } else {
+                                    containerSize.height - itemSize.height - it.second.y
+                                }
+                            )
+                    }
+                }
+            }
+        )
+        if (crossAxis != null) {
+            val actualCross = expected.map {
+                it.first to actualBounds.getValue(it.first.toString()).topLeft
+                    .let { offset -> if (isVertical) offset.x else offset.y }
+            }
+            Truth.assertWithMessage(
+                "CrossAxis" + if (fraction != null) "for fraction=$fraction" else ""
+            )
+                .that(actualCross)
+                .isEqualTo(crossAxis.map { it.first to it.second.roundToInt() })
+        }
+    }
+
+    private fun assertLayoutInfoPositions(vararg offsets: Pair<Any, Offset>) {
+        rule.runOnIdle {
+            assertThat(visibleItemsOffsets).isEqualTo(offsets.map { it.first to it.second.round() })
+        }
+    }
+
+    private val visibleItemsOffsets: List<Pair<Any, IntOffset>>
+        get() = state.layoutInfo.visibleItemsInfo.map {
+            it.key to it.offset
+        }
+
+    private fun onAnimationFrame(duration: Long = Duration, onFrame: (fraction: Float) -> Unit) {
+        require(duration.mod(FrameDuration) == 0L)
+        rule.waitForIdle()
+        rule.mainClock.advanceTimeByFrame()
+        var expectedTime = rule.mainClock.currentTime
+        for (i in 0..duration step FrameDuration) {
+            val fraction = i / duration.toFloat()
+            onFrame(fraction)
+            rule.mainClock.advanceTimeBy(FrameDuration)
+            expectedTime += FrameDuration
+            assertThat(expectedTime).isEqualTo(rule.mainClock.currentTime)
+        }
+    }
+
+    @Composable
+    private fun LazyStaggeredGrid(
+        cells: Int,
+        minSize: Dp = 0.dp,
+        maxSize: Dp = containerSizeDp,
+        startIndex: Int = 0,
+        startPadding: Dp = 0.dp,
+        endPadding: Dp = 0.dp,
+        spacing: Dp = 0.dp,
+        content: LazyStaggeredGridScope.() -> Unit
+    ) {
+        state = rememberLazyStaggeredGridState(startIndex)
+        if (isVertical) {
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(cells),
+                Modifier
+                    .requiredHeightIn(minSize, maxSize)
+                    .requiredWidth(itemSizeDp * cells)
+                    .testTag(ContainerTag),
+                state = state,
+                verticalItemSpacing = spacing,
+                reverseLayout = reverseLayout,
+                contentPadding = PaddingValues(top = startPadding, bottom = endPadding),
+                content = content
+            )
+        } else {
+            LazyHorizontalStaggeredGrid(
+                StaggeredGridCells.Fixed(cells),
+                Modifier
+                    .requiredWidthIn(minSize, maxSize)
+                    .requiredHeight(itemSizeDp * cells)
+                    .testTag(ContainerTag),
+                state = state,
+                reverseLayout = reverseLayout,
+                horizontalItemSpacing = spacing,
+                contentPadding = PaddingValues(start = startPadding, end = endPadding),
+                content = content
+            )
+        }
+    }
+
+    @Composable
+    private fun LazyStaggeredGridItemScope.Item(
+        tag: Int,
+        size: Dp = itemSizeDp,
+        animSpec: FiniteAnimationSpec<IntOffset>? = AnimSpec
+    ) {
+        Box(
+            if (animSpec != null) {
+                Modifier.animateItemPlacement(animSpec)
+            } else {
+                Modifier
+            }
+                .then(
+                    if (isVertical) {
+                        Modifier.requiredHeight(size)
+                    } else {
+                        Modifier.requiredWidth(size)
+                    }
+                )
+                .testTag(tag.toString())
+        )
+    }
+
+    private fun SemanticsNodeInteraction.assertMainAxisSizeIsEqualTo(
+        expected: Dp
+    ): SemanticsNodeInteraction {
+        return if (isVertical) assertHeightIsEqualTo(expected) else assertWidthIsEqualTo(expected)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = arrayOf(
+            Config(isVertical = true, reverseLayout = false),
+            Config(isVertical = false, reverseLayout = false),
+            Config(isVertical = true, reverseLayout = true),
+            Config(isVertical = false, reverseLayout = true),
+        )
+
+        class Config(
+            val isVertical: Boolean,
+            val reverseLayout: Boolean
+        ) {
+            override fun toString() =
+                (if (isVertical) "LazyVerticalGrid" else "LazyHorizontalGrid") +
+                    (if (reverseLayout) "(reverse)" else "")
+        }
+    }
+}
+
+private val FrameDuration = 16L
+private val Duration = 64L // 4 frames, so we get 0f, 0.25f, 0.5f, 0.75f and 1f fractions
+private val AnimSpec = tween<IntOffset>(Duration.toInt(), easing = LinearEasing)
+private val ContainerTag = "container"
+private val NodesWithTagMatcher = SemanticsMatcher("NodesWithTag") {
+    it.config.contains(SemanticsProperties.TestTag)
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
index 627f6fb..d68ad55 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
@@ -69,16 +69,22 @@
         itemSizeDp = with(rule.density) {
             itemSizePx.toDp()
         }
+    }
+
+    private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
         rule.setContent {
-            scope = rememberCoroutineScope()
             state = rememberLazyStaggeredGridState()
-            TestContent()
+            scope = rememberCoroutineScope()
+            TestContent(with(rule.density) { spacingPx.toDp() })
         }
         rule.waitForIdle()
+        runBlocking {
+            assertBlock()
+        }
     }
 
     @Test
-    fun animateScrollBy() = runBlocking {
+    fun animateScrollBy() = testScroll {
         val scrollDistance = 320
 
         val expectedIndex = scrollDistance * 2 / itemSizePx // resolves to 6
@@ -92,7 +98,7 @@
     }
 
     @Test
-    fun animateScrollToItem_positiveOffset() = runBlocking {
+    fun animateScrollToItem_positiveOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, 10)
         }
@@ -101,7 +107,7 @@
     }
 
     @Test
-    fun animateScrollToItem_positiveOffset_largerThanItem() = runBlocking {
+    fun animateScrollToItem_positiveOffset_largerThanItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, 150)
         }
@@ -110,7 +116,7 @@
     }
 
     @Test
-    fun animateScrollToItem_negativeOffset() = runBlocking {
+    fun animateScrollToItem_negativeOffset() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, -10)
         }
@@ -119,7 +125,7 @@
     }
 
     @Test
-    fun animateScrollToItem_beforeFirstItem() = runBlocking {
+    fun animateScrollToItem_beforeFirstItem() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(10)
             state.animateScrollToItem(0, -10)
@@ -129,7 +135,7 @@
     }
 
     @Test
-    fun animateScrollToItem_afterLastItem() {
+    fun animateScrollToItem_afterLastItem() = testScroll {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(100)
         }
@@ -139,7 +145,7 @@
     }
 
     @Test
-    fun animateScrollToItem_toFullSpan() {
+    fun animateScrollToItem_toFullSpan() = testScroll {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(50, 10)
         }
@@ -149,7 +155,7 @@
     }
 
     @Test
-    fun animateScrollToItem_toFullSpan_andBack() {
+    fun animateScrollToItem_toFullSpan_andBack() = testScroll {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(50, 10)
         }
@@ -164,32 +170,32 @@
     }
 
     @Test
-    fun animateScrollToItem_inBounds() {
+    fun animateScrollToItem_inBounds() = testScroll {
         assertSpringAnimation(2)
     }
 
     @Test
-    fun animateScrollToItem_inBounds_withOffset() {
+    fun animateScrollToItem_inBounds_withOffset() = testScroll {
         assertSpringAnimation(2, itemSizePx / 2)
     }
 
     @Test
-    fun animateScrollToItem_outOfBounds() {
+    fun animateScrollToItem_outOfBounds() = testScroll {
         assertSpringAnimation(10)
     }
 
     @Test
-    fun animateScrollToItem_firstItem() {
+    fun animateScrollToItem_firstItem() = testScroll {
         assertSpringAnimation(fromIndex = 10, fromOffset = 10, toIndex = 0)
     }
 
     @Test
-    fun animateScrollToItem_firstItem_toOffset() {
+    fun animateScrollToItem_firstItem_toOffset() = testScroll {
         assertSpringAnimation(fromIndex = 10, fromOffset = 10, toIndex = 0, toOffset = 10)
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(20, -itemSizePx * 3)
         }
@@ -199,7 +205,7 @@
     }
 
     @Test
-    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+    fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(20)
             state.animateScrollToItem(0, itemSizePx * 3)
@@ -208,11 +214,32 @@
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
     }
 
+    @Test
+    fun animateScrollToItem_outOfBounds_withSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(20, spacingPx = 10)
+    }
+
+    @Test
+    fun animateScrollToItem_outOfBounds_withNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(20, spacingPx = -10)
+    }
+
+    @Test
+    fun animateScrollToItem_backwards_withSpacing() = testScroll(spacingPx = 10) {
+        assertSpringAnimation(toIndex = 0, fromIndex = 20, spacingPx = 10)
+    }
+
+    @Test
+    fun animateScrollToItem_backwards_withNegativeSpacing() = testScroll(spacingPx = -10) {
+        assertSpringAnimation(toIndex = 0, fromIndex = 20, spacingPx = -10)
+    }
+
     private fun assertSpringAnimation(
         toIndex: Int,
         toOffset: Int = 0,
         fromIndex: Int = 0,
-        fromOffset: Int = 0
+        fromOffset: Int = 0,
+        spacingPx: Int = 0
     ) {
         if (fromIndex != 0 || fromOffset != 0) {
             rule.runOnIdle {
@@ -236,8 +263,10 @@
             Thread.sleep(5)
         }
 
-        val startOffset = (fromIndex / 2 * itemSizePx + fromOffset).toFloat()
-        val endOffset = (toIndex / 2 * itemSizePx + toOffset).toFloat()
+        val itemSizeWSpacing = spacingPx + itemSizePx
+        val startOffset = (fromIndex / 2 * itemSizeWSpacing + fromOffset).toFloat()
+        val endOffset = (toIndex / 2 * itemSizeWSpacing + toOffset).toFloat()
+
         val spec = FloatSpringSpec()
 
         val duration =
@@ -250,9 +279,10 @@
             val expectedValue =
                 spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
             val actualValue =
-                (state.firstVisibleItemIndex / 2 * itemSizePx + state.firstVisibleItemScrollOffset)
+                (state.firstVisibleItemIndex / 2 * itemSizeWSpacing +
+                    state.firstVisibleItemScrollOffset)
             assertWithMessage(
-                "On animation frame at $i index=${state.firstVisibleItemIndex} " +
+                "On animation frame at ${i}ms index=${state.firstVisibleItemIndex} " +
                     "offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
             ).that(actualValue).isEqualTo(expectedValue.roundToInt(), tolerance = 1)
 
@@ -266,11 +296,12 @@
     }
 
     @Composable
-    private fun TestContent() {
+    private fun TestContent(spacingDp: Dp) {
         LazyStaggeredGrid(
             lanes = 2,
             state = state,
-            modifier = Modifier.axisSize(itemSizeDp * 2, itemSizeDp * 5)
+            modifier = Modifier.axisSize(itemSizeDp * 2, itemSizeDp * 5),
+            mainAxisSpacing = spacingDp
         ) {
             items(
                 count = 100,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
index 74e519c..01f2c1c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
@@ -48,7 +48,6 @@
 
 package androidx.compose.foundation.lazy.staggeredgrid
 
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -62,13 +61,12 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
-@OptIn(ExperimentalFoundationApi::class)
 @MediumTest
 @RunWith(Parameterized::class)
 class LazyStaggeredGridArrangementsTest(
@@ -220,6 +218,55 @@
         rule.onNodeWithTag("2")
             .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
             .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+    }
+
+    @Test
+    fun negativeSpacing_withContentPadding_itemsVisible() {
+        state = LazyStaggeredGridState()
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                modifier = Modifier
+                    .testTag(LazyStaggeredGrid)
+                    .axisSize(itemSizeDp * 2, itemSizeDp * 5),
+                state = state,
+                mainAxisSpacing = -itemSizeDp,
+                contentPadding = PaddingValues(beforeContent = itemSizeDp)
+            ) {
+                items(100) {
+                    BasicText(
+                        text = "$it",
+                        modifier = Modifier
+                            .testTag("$it")
+                            .mainAxisSize(itemSizeDp * 2)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp * 2)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        state.scrollBy(itemSizeDp)
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
     }
 
     @Test
@@ -245,8 +292,8 @@
         }
 
         rule.runOnIdle {
-            Truth.assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            Truth.assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt
new file mode 100644
index 0000000..1b82ba8
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt
@@ -0,0 +1,707 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.BeyondBoundsLayout
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.After
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Before
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Below
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Left
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Right
+import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection.Rtl
+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
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalComposeUiApi::class)
+@MediumTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridBeyondBoundsTest(param: Param) {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    // We need to wrap the inline class parameter in another class because Java can't instantiate
+    // the inline class.
+    class Param(
+        val beyondBoundsLayoutDirection: BeyondBoundsLayout.LayoutDirection,
+        val reverseLayout: Boolean,
+        val layoutDirection: LayoutDirection,
+    ) {
+        override fun toString() = "beyondBoundsLayoutDirection=$beyondBoundsLayoutDirection " +
+            "reverseLayout=$reverseLayout " +
+            "layoutDirection=$layoutDirection"
+    }
+
+    private val beyondBoundsLayoutDirection = param.beyondBoundsLayoutDirection
+    private val reverseLayout = param.reverseLayout
+    private val layoutDirection = param.layoutDirection
+    private val placedItems = mutableSetOf<Int>()
+    private var beyondBoundsLayout: BeyondBoundsLayout? = null
+    private lateinit var lazyStaggeredGridState: LazyStaggeredGridState
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun initParameters() = buildList {
+            for (beyondBoundsLayoutDirection in listOf(Left, Right, Above, Below, Before, After)) {
+                for (reverseLayout in listOf(false, true)) {
+                    for (layoutDirection in listOf(Ltr, Rtl)) {
+                        add(Param(beyondBoundsLayoutDirection, reverseLayout, layoutDirection))
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun onlyOneVisibleItemIsPlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 10.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0)
+            assertThat(visibleItems).containsExactly(0)
+        }
+    }
+
+    @Test
+    fun onlyTwoVisibleItemsArePlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 20.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0, 1)
+            assertThat(visibleItems).containsExactly(0, 1)
+        }
+    }
+
+    @Test
+    fun onlyThreeVisibleItemsArePlaced() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+            items(100) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(0, 1, 2)
+            assertThat(visibleItems).containsExactly(0, 1, 2)
+        }
+    }
+
+    @Test
+    fun emptyLazyList_doesNotCrash() {
+        // Arrange.
+        var addItems by mutableStateOf(true)
+        lateinit var beyondBoundsLayoutRef: BeyondBoundsLayout
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+            if (addItems) {
+                item {
+                    Box(
+                        Modifier.modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            beyondBoundsLayoutRef = beyondBoundsLayout!!
+            addItems = false
+        }
+
+        // Act.
+        val hasMoreContent = rule.runOnIdle {
+            beyondBoundsLayoutRef.layout(beyondBoundsLayoutDirection) {
+                hasMoreContent
+            }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(hasMoreContent).isFalse()
+        }
+    }
+
+    @Test
+    fun oneExtraItemBeyondVisibleBounds() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(Modifier
+                    .size(10.toDp())
+                    .onPlaced { placedItems += 5 }
+                    .modifierLocalConsumer {
+                        beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                    }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that the beyond bounds items are present.
+                if (expectedExtraItemsBeforeVisibleBounds()) {
+                    assertThat(placedItems).containsExactly(4, 5, 6, 7)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                } else {
+                    assertThat(placedItems).containsExactly(5, 6, 7, 8)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun oneExtraItemBeyondVisibleBounds_multipleCells() {
+        val itemSize = 50
+        val itemSizeDp = itemSize.toDp()
+        // Arrange.
+        rule.setLazyContent(cells = 2, size = itemSizeDp * 3, firstVisibleItem = 10) {
+            // item | item  | x5
+            // item | local | x1
+            // item | item  | x5
+            items(11) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp)
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(Modifier
+                    .size(itemSizeDp)
+                    .onPlaced { placedItems += 11 }
+                    .modifierLocalConsumer {
+                        beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                    }
+                )
+            }
+            items(10) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp)
+                        .onPlaced { placedItems += index + 12 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that the beyond bounds items are present.
+                if (expectedExtraItemsBeforeVisibleBounds()) {
+                    assertThat(placedItems).containsExactly(9, 10, 11, 12, 13, 14, 15)
+                    assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+                } else {
+                    assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15, 16)
+                    assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15)
+            assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+        }
+    }
+
+    @Test
+    fun oneExtraItemBeyondVisibleBounds_multipleCells_staggered() {
+        val itemSize = 50
+        val itemSizeDp = itemSize.toDp()
+        // Arrange.
+        rule.setLazyContent(cells = 3, size = itemSizeDp * 2, firstVisibleItem = 4) {
+            // -------------
+            // |   | 1 |   |
+            // | 0 |---| 2 |
+            // |   | 3 |   |
+            // |-----------|
+            // |     4     |
+            // |-----------|
+            // |   | 6 |   |
+            // | 5 |---| 7 |
+            // |   | 8 |   |
+            // -------------
+            items(4) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp * if (index % 2 == 0) 2f else 1f)
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item(span = StaggeredGridItemSpan.FullLine) {
+                Box(Modifier
+                    .size(itemSizeDp)
+                    .onPlaced { placedItems += 4 }
+                    .modifierLocalConsumer {
+                        beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                    }
+                )
+            }
+            items(4) { index ->
+                Box(
+                    Modifier
+                        .size(itemSizeDp * if (index % 2 == 0) 2f else 1f)
+                        .onPlaced { placedItems += index + 5 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that the beyond bounds items are present.
+                if (expectedExtraItemsBeforeVisibleBounds()) {
+                    assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+                    assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+                } else {
+                    assertThat(placedItems).containsExactly(4, 5, 6, 7, 8)
+                    assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(4, 5, 6, 7)
+            assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+        }
+    }
+
+    @Test
+    fun twoExtraItemsBeyondVisibleBounds() {
+        // Arrange.
+        var extraItemCount = 2
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += 5 }
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                if (--extraItemCount > 0) {
+                    placedItems.clear()
+                    // Return null to continue the search.
+                    null
+                } else {
+                    // Assert that the beyond bounds items are present.
+                    if (expectedExtraItemsBeforeVisibleBounds()) {
+                        assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    } else {
+                        assertThat(placedItems).containsExactly(5, 6, 7, 8, 9)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    placedItems.clear()
+                    // Return true to stop the search.
+                    true
+                }
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun allBeyondBoundsItemsInSpecifiedDirection() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                        .onPlaced { placedItems += 5 }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index + 6
+                        }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                if (hasMoreContent) {
+                    placedItems.clear()
+                    // Just return null so that we keep adding more items till we reach the end.
+                    null
+                } else {
+                    // Assert that the beyond bounds items are present.
+                    if (expectedExtraItemsBeforeVisibleBounds()) {
+                        assertThat(placedItems).containsExactly(0, 1, 2, 3, 4, 5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    } else {
+                        assertThat(placedItems).containsExactly(5, 6, 7, 8, 9, 10)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    placedItems.clear()
+                    // Return true to end the search.
+                    true
+                }
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    @Test
+    fun beyondBoundsLayoutRequest_inDirectionPerpendicularToLazyListOrientation() {
+        // Arrange.
+        var beyondBoundsLayoutCount = 0
+        rule.setLazyContentInPerpendicularDirection(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += 5 }
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced { placedItems += index + 6 }
+                )
+            }
+        }
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+            placedItems.clear()
+        }
+
+        // Act.
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                beyondBoundsLayoutCount++
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Above, Below -> {
+                        assertThat(placedItems).containsExactly(5, 6, 7)
+                        assertThat(visibleItems).containsExactly(5, 6, 7)
+                    }
+                    Before, After -> {
+                        if (expectedExtraItemsBeforeVisibleBounds()) {
+                            assertThat(placedItems).containsExactly(4, 5, 6, 7)
+                            assertThat(visibleItems).containsExactly(5, 6, 7)
+                        } else {
+                            assertThat(placedItems).containsExactly(5, 6, 7, 8)
+                            assertThat(visibleItems).containsExactly(5, 6, 7)
+                        }
+                    }
+                }
+                placedItems.clear()
+                // Just return true so that we stop as soon as we run this once.
+                // This should result in one extra item being added.
+                true
+            }
+        }
+
+        rule.runOnIdle {
+            when (beyondBoundsLayoutDirection) {
+                Left, Right, Above, Below -> {
+                    assertThat(beyondBoundsLayoutCount).isEqualTo(0)
+                }
+                Before, After -> {
+                    assertThat(beyondBoundsLayoutCount).isEqualTo(1)
+
+                    // Assert that the beyond bounds items are removed.
+                    assertThat(placedItems).containsExactly(5, 6, 7)
+                    assertThat(visibleItems).containsExactly(5, 6, 7)
+                }
+                else -> error("Unsupported BeyondBoundsLayoutDirection")
+            }
+        }
+    }
+
+    @Test
+    fun returningNullDoesNotCauseInfiniteLoop() {
+        // Arrange.
+        rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index
+                        }
+                )
+            }
+            item {
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .modifierLocalConsumer {
+                            beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+                        }
+                        .onPlaced { placedItems += 5 }
+                )
+            }
+            items(5) { index ->
+                Box(
+                    Modifier
+                        .size(10.toDp())
+                        .onPlaced {
+                            placedItems += index + 6
+                        }
+                )
+            }
+        }
+        rule.runOnIdle { placedItems.clear() }
+
+        // Act.
+        var count = 0
+        rule.runOnUiThread {
+            beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+                // Assert that we don't keep iterating when there is no ending condition.
+                assertThat(count++).isLessThan(lazyStaggeredGridState.layoutInfo.totalItemsCount)
+                placedItems.clear()
+                // Always return null to continue the search.
+                null
+            }
+        }
+
+        // Assert that the beyond bounds items are removed.
+        rule.runOnIdle {
+            assertThat(placedItems).containsExactly(5, 6, 7)
+            assertThat(visibleItems).containsExactly(5, 6, 7)
+        }
+    }
+
+    private fun ComposeContentTestRule.setLazyContent(
+        size: Dp,
+        firstVisibleItem: Int,
+        cells: Int = 1,
+        content: LazyStaggeredGridScope.() -> Unit
+    ) {
+        setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                lazyStaggeredGridState = rememberLazyStaggeredGridState(firstVisibleItem)
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Before, After ->
+                        LazyHorizontalStaggeredGrid(
+                            rows = StaggeredGridCells.Fixed(cells),
+                            modifier = Modifier.size(size),
+                            state = lazyStaggeredGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    Above, Below ->
+                        LazyVerticalStaggeredGrid(
+                            columns = StaggeredGridCells.Fixed(cells),
+                            modifier = Modifier.size(size),
+                            state = lazyStaggeredGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    else -> unsupportedDirection()
+                }
+            }
+        }
+    }
+
+    private fun ComposeContentTestRule.setLazyContentInPerpendicularDirection(
+        size: Dp,
+        firstVisibleItem: Int,
+        content: LazyStaggeredGridScope.() -> Unit
+    ) {
+        setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                lazyStaggeredGridState = rememberLazyStaggeredGridState(firstVisibleItem)
+                when (beyondBoundsLayoutDirection) {
+                    Left, Right, Before, After ->
+                        LazyVerticalStaggeredGrid(
+                            columns = StaggeredGridCells.Fixed(1),
+                            modifier = Modifier.size(size),
+                            state = lazyStaggeredGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    Above, Below ->
+                        LazyHorizontalStaggeredGrid(
+                            rows = StaggeredGridCells.Fixed(1),
+                            modifier = Modifier.size(size),
+                            state = lazyStaggeredGridState,
+                            reverseLayout = reverseLayout,
+                            content = content
+                        )
+                    else -> unsupportedDirection()
+                }
+            }
+        }
+    }
+
+    private fun Int.toDp(): Dp = with(rule.density) { toDp() }
+
+    private val visibleItems: List<Int>
+        get() = lazyStaggeredGridState.layoutInfo.visibleItemsInfo.map { it.index }
+
+    private fun expectedExtraItemsBeforeVisibleBounds() = when (beyondBoundsLayoutDirection) {
+        Right -> if (layoutDirection == Ltr) reverseLayout else !reverseLayout
+        Left -> if (layoutDirection == Ltr) !reverseLayout else reverseLayout
+        Above -> !reverseLayout
+        Below -> reverseLayout
+        After -> false
+        Before -> true
+        else -> error("Unsupported BeyondBoundsDirection")
+    }
+
+    private fun unsupportedDirection(): Nothing = error(
+        "Lazy list does not support beyond bounds layout for the specified direction"
+    )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
index 54057c2..6af08de 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
@@ -397,9 +397,40 @@
 
         // bottom padding applies instead of the top
         rule.onNodeWithTag("0")
-            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 3)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
         rule.onNodeWithTag("1")
-            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 3)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
+    }
+
+    @Test
+    fun contentPadding_isPreserved() {
+        val state = LazyStaggeredGridState()
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                modifier = Modifier
+                    .axisSize(itemSize * 2, itemSize * 5)
+                    .testTag(StaggeredGridTag),
+                contentPadding = PaddingValues(afterContent = itemSize * 2),
+                reverseLayout = true,
+                state = state
+            ) {
+                items(6) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+        }
+
+        // bottom padding applies instead of the top
+        rule.onNodeWithTag("0")
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
+        rule.onNodeWithTag("1")
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
index 879e105..33b931a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
@@ -77,10 +77,9 @@
         return ExcludeFromSystemGestureNode(exclusion)
     }
 
-    override fun update(node: ExcludeFromSystemGestureNode): ExcludeFromSystemGestureNode =
-        node.also {
-            it.exclusion = exclusion
-        }
+    override fun update(node: ExcludeFromSystemGestureNode) {
+        node.exclusion = exclusion
+    }
 
     override fun hashCode(): Int {
         return exclusion.hashCode()
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
index 1877cfa..47f6759 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
@@ -26,7 +26,6 @@
 import android.view.View
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
-import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.text2.input.TextEditFilter
@@ -230,7 +229,7 @@
          * and grab the [InputConnection] to inject commands.
          */
         @TestOnly
-        @RestrictTo(RestrictTo.Scope.TESTS)
+        @VisibleForTesting
         fun setInputConnectionCreatedListenerForTests(
             listener: ((EditorInfo, InputConnection) -> Unit)?
         ) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
index 71b3288..caccd1a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
@@ -24,7 +24,7 @@
 import android.view.inputmethod.ExtractedText
 import android.view.inputmethod.InputMethodManager
 import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
 import androidx.core.view.SoftwareKeyboardControllerCompat
 import org.jetbrains.annotations.TestOnly
 
@@ -88,7 +88,7 @@
  * avoid breaking unrelated tests.
  */
 @TestOnly
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
 internal fun overrideComposeInputMethodManagerFactoryForTests(
     factory: (View) -> ComposeInputMethodManager
 ): (View) -> ComposeInputMethodManager {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
index 655d496..15ab3ed 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
@@ -90,7 +90,7 @@
         orientation = orientation,
     )
 
-    override fun update(node: TextFieldCoreModifierNode): TextFieldCoreModifierNode {
+    override fun update(node: TextFieldCoreModifierNode) {
         node.updateNode(
             isFocused = isFocused,
             textLayoutState = textLayoutState,
@@ -100,7 +100,6 @@
             scrollState = scrollState,
             orientation = orientation,
         )
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
index b1955d7..1f81a01 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
@@ -96,7 +96,7 @@
         singleLine = singleLine,
     )
 
-    override fun update(node: TextFieldDecoratorModifierNode): TextFieldDecoratorModifierNode {
+    override fun update(node: TextFieldDecoratorModifierNode) {
         node.updateNode(
             textFieldState = textFieldState,
             textLayoutState = textLayoutState,
@@ -108,7 +108,6 @@
             keyboardActions = keyboardActions,
             singleLine = singleLine,
         )
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -137,7 +136,7 @@
     KeyInputModifierNode,
     CompositionLocalConsumerModifierNode {
 
-    private val pointerInputNode = SuspendingPointerInputModifierNode {
+    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
         detectTapAndPress(onTap = {
             if (!isFocused) {
                 requestFocus()
@@ -145,10 +144,7 @@
                 textInputSession?.showSoftwareKeyboard()
             }
         })
-    }
-        // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
-        //  block
-        .also { delegated { it.node } }
+    })
 
     var keyboardOptions: KeyboardOptions = keyboardOptions.withDefaultsFrom(filter?.keyboardOptions)
         private set
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 ab0b5f5..910e79f 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
@@ -104,12 +104,11 @@
         )
     }
 
-    override fun update(node: BackgroundNode): BackgroundNode {
+    override fun update(node: BackgroundNode) {
         node.color = color
         node.brush = brush
         node.alpha = alpha
         node.shape = shape
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 62f02ee..099246b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -421,8 +421,8 @@
         onClick
     )
 
-    override fun update(node: ClickableNode) = node.also {
-        it.update(interactionSource, enabled, onClickLabel, role, onClick)
+    override fun update(node: ClickableNode) {
+        node.update(interactionSource, enabled, onClickLabel, role, onClick)
     }
 
     // Defined in the factory functions with inspectable
@@ -474,8 +474,8 @@
         onDoubleClick
     )
 
-    override fun update(node: CombinedClickableNode) = node.also {
-        it.update(
+    override fun update(node: CombinedClickableNode) {
+        node.update(
             interactionSource,
             enabled,
             onClickLabel,
@@ -528,7 +528,7 @@
     role: Role?,
     onClick: () -> Unit
 ) : AbstractClickableNode(interactionSource, enabled, onClickLabel, role, onClick) {
-    override val clickableSemanticsNode = delegated {
+    override val clickableSemanticsNode = delegate(
         ClickableSemanticsNode(
             enabled = enabled,
             role = role,
@@ -537,16 +537,16 @@
             onLongClick = null,
             onLongClickLabel = null
         )
-    }
+    )
 
-    override val clickablePointerInputNode = delegated {
+    override val clickablePointerInputNode = delegate(
         ClickablePointerInputNode(
             enabled = enabled,
             interactionSource = interactionSource,
             onClick = onClick,
             interactionData = interactionData
         )
-    }
+    )
 
     fun update(
         interactionSource: MutableInteractionSource,
@@ -582,7 +582,7 @@
     private var onLongClick: (() -> Unit)?,
     onDoubleClick: (() -> Unit)?
 ) : AbstractClickableNode(interactionSource, enabled, onClickLabel, role, onClick) {
-    override val clickableSemanticsNode = delegated {
+    override val clickableSemanticsNode = delegate(
         ClickableSemanticsNode(
             enabled = enabled,
             role = role,
@@ -591,9 +591,9 @@
             onLongClickLabel = onLongClickLabel,
             onLongClick = onLongClick
         )
-    }
+    )
 
-    override val clickablePointerInputNode = delegated {
+    override val clickablePointerInputNode = delegate(
         CombinedClickablePointerInputNode(
             enabled = enabled,
             interactionSource = interactionSource,
@@ -602,7 +602,7 @@
             onLongClick,
             onDoubleClick
         )
-    }
+    )
 
     fun update(
         interactionSource: MutableInteractionSource,
@@ -757,8 +757,8 @@
         onClick = onClick
     )
 
-    override fun update(node: ClickableSemanticsNode) = node.also {
-        it.update(enabled, onClickLabel, role, onClick, onLongClickLabel, onLongClick)
+    override fun update(node: ClickableSemanticsNode) {
+        node.update(enabled, onClickLabel, role, onClick, onLongClickLabel, onLongClick)
     }
 
     override fun InspectorInfo.inspectableProperties() = Unit
@@ -846,10 +846,7 @@
         ModifierLocalScrollableContainer.current || isComposeRootInScrollableContainer()
     }
 
-    private val pointerInputNode = SuspendingPointerInputModifierNode { pointerInput() }
-        // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
-        //  block
-        .also { delegated { it.node } }
+    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode { pointerInput() })
 
     protected abstract suspend fun PointerInputScope.pointerInput()
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index 19838a9..bb1c694 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -137,7 +137,7 @@
     object : ModifierNodeElement<FocusableInNonTouchMode>() {
         override fun create(): FocusableInNonTouchMode = FocusableInNonTouchMode()
 
-        override fun update(node: FocusableInNonTouchMode): FocusableInNonTouchMode = node
+        override fun update(node: FocusableInNonTouchMode) {}
 
         override fun hashCode(): Int = System.identityHashCode(this)
 
@@ -168,8 +168,8 @@
     override fun create(): FocusableNode =
         FocusableNode(interactionSource)
 
-    override fun update(node: FocusableNode) = node.also {
-        it.update(interactionSource)
+    override fun update(node: FocusableNode) {
+        node.update(interactionSource)
     }
 
     override fun equals(other: Any?): Boolean {
@@ -199,12 +199,11 @@
 
     private var focusState: FocusState? = null
 
-    private val focusableSemanticsNode = delegated { FocusableSemanticsNode() }
-
+    private val focusableSemanticsNode = delegate(FocusableSemanticsNode())
     // (lpf) could we remove this if interactionsource is null?
-    private val focusableInteractionNode = delegated { FocusableInteractionNode(interactionSource) }
-    private val focusablePinnableContainer = delegated { FocusablePinnableContainerNode() }
-    private val focusedBoundsNode = delegated { FocusedBoundsNode() }
+    private val focusableInteractionNode = delegate(FocusableInteractionNode(interactionSource))
+    private val focusablePinnableContainer = delegate(FocusablePinnableContainerNode())
+    private val focusedBoundsNode = delegate(FocusedBoundsNode())
 
     // Focusables have a few different cases where they need to make sure they stay visible:
     //
@@ -218,9 +217,9 @@
     //    See aosp/1964580.
     private val bringIntoViewRequester = BringIntoViewRequester()
 
-    private val bringIntoViewRequesterNode = delegated {
+    private val bringIntoViewRequesterNode = delegate(
         BringIntoViewRequesterNode(bringIntoViewRequester)
-    }
+    )
 
     // TODO(levima) Remove this once delegation can propagate this events on its own
     override fun onPlaced(coordinates: LayoutCoordinates) =
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/FocusedBounds.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/FocusedBounds.kt
index adecbd5..5212dff 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/FocusedBounds.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/FocusedBounds.kt
@@ -51,8 +51,8 @@
 ) : ModifierNodeElement<FocusedBoundsObserverNode>() {
     override fun create(): FocusedBoundsObserverNode = FocusedBoundsObserverNode(onPositioned)
 
-    override fun update(node: FocusedBoundsObserverNode): FocusedBoundsObserverNode = node.also {
-        it.onPositioned = onPositioned
+    override fun update(node: FocusedBoundsObserverNode) {
+        node.onPositioned = onPositioned
     }
 
     override fun hashCode(): Int = onPositioned.hashCode()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
index 4f2ae94..cfc7267 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
@@ -47,8 +47,8 @@
 ) : ModifierNodeElement<HoverableNode>() {
     override fun create() = HoverableNode(interactionSource)
 
-    override fun update(node: HoverableNode) = node.apply {
-        updateInteractionSource(interactionSource)
+    override fun update(node: HoverableNode) {
+        node.updateInteractionSource(interactionSource)
     }
 
     override fun hashCode(): Int {
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 01fdca8..1d58733 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
@@ -338,10 +338,10 @@
         )
     }
 
-    override fun update(node: ScrollingLayoutNode): ScrollingLayoutNode = node.also {
-        it.scrollerState = scrollState
-        it.isReversed = isReversed
-        it.isVertical = isVertical
+    override fun update(node: ScrollingLayoutNode) {
+        node.scrollerState = scrollState
+        node.isReversed = isReversed
+        node.isVertical = isVertical
     }
 
     override fun hashCode(): Int {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
index 99bfce38..de09cb5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
@@ -26,36 +26,37 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
+import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
 import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.input.pointer.util.addPointerInputChange
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.math.sign
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.SendChannel
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
 
 /**
  * State of [draggable]. Allows for a granular control of how deltas are consumed by the user as
@@ -183,7 +184,7 @@
     onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
     onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {},
     reverseDirection: Boolean = false
-): Modifier = draggable(
+): Modifier = this then DraggableElement(
     state = state,
     orientation = orientation,
     enabled = enabled,
@@ -195,18 +196,76 @@
     canDrag = { true }
 )
 
-internal fun Modifier.draggable(
-    state: DraggableState,
-    canDrag: (PointerInputChange) -> Boolean,
-    orientation: Orientation,
-    enabled: Boolean = true,
-    interactionSource: MutableInteractionSource? = null,
-    startDragImmediately: () -> Boolean,
-    onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
-    onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit = {},
-    reverseDirection: Boolean = false
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
+internal class DraggableElement(
+    private val state: DraggableState,
+    private val canDrag: (PointerInputChange) -> Boolean,
+    private val orientation: Orientation,
+    private val enabled: Boolean,
+    private val interactionSource: MutableInteractionSource?,
+    private val startDragImmediately: () -> Boolean,
+    private val onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+    private val onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+    private val reverseDirection: Boolean
+) : ModifierNodeElement<DraggableNode>() {
+    override fun create(): DraggableNode = DraggableNode(
+        state,
+        canDrag,
+        orientation,
+        enabled,
+        interactionSource,
+        startDragImmediately,
+        onDragStarted,
+        onDragStopped,
+        reverseDirection
+    )
+
+    override fun update(node: DraggableNode) {
+        node.update(
+            state,
+            canDrag,
+            orientation,
+            enabled,
+            interactionSource,
+            startDragImmediately,
+            onDragStarted,
+            onDragStopped,
+            reverseDirection
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as DraggableElement
+
+        if (state != other.state) return false
+        if (canDrag != other.canDrag) return false
+        if (orientation != other.orientation) return false
+        if (enabled != other.enabled) return false
+        if (interactionSource != other.interactionSource) return false
+        if (startDragImmediately != other.startDragImmediately) return false
+        if (onDragStarted != other.onDragStarted) return false
+        if (onDragStopped != other.onDragStopped) return false
+        if (reverseDirection != other.reverseDirection) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = state.hashCode()
+        result = 31 * result + canDrag.hashCode()
+        result = 31 * result + orientation.hashCode()
+        result = 31 * result + enabled.hashCode()
+        result = 31 * result + (interactionSource?.hashCode() ?: 0)
+        result = 31 * result + startDragImmediately.hashCode()
+        result = 31 * result + onDragStarted.hashCode()
+        result = 31 * result + onDragStopped.hashCode()
+        result = 31 * result + reverseDirection.hashCode()
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
         name = "draggable"
         properties["canDrag"] = canDrag
         properties["orientation"] = orientation
@@ -218,56 +277,33 @@
         properties["onDragStopped"] = onDragStopped
         properties["state"] = state
     }
-) {
-    val draggedInteraction = remember { mutableStateOf<DragInteraction.Start?>(null) }
-    DisposableEffect(interactionSource) {
-        onDispose {
-            draggedInteraction.value?.let { interaction ->
-                interactionSource?.tryEmit(DragInteraction.Cancel(interaction))
-                draggedInteraction.value = null
-            }
-        }
-    }
-    val channel = remember { Channel<DragEvent>(capacity = Channel.UNLIMITED) }
-    val startImmediatelyState = rememberUpdatedState(startDragImmediately)
-    val canDragState = rememberUpdatedState(canDrag)
-    val dragLogic by rememberUpdatedState(
-        DragLogic(onDragStarted, onDragStopped, draggedInteraction, interactionSource)
-    )
-    LaunchedEffect(state) {
-        while (isActive) {
-            var event = channel.receive()
-            if (event !is DragStarted) continue
-            with(dragLogic) { processDragStart(event as DragStarted) }
-            try {
-                state.drag(MutatePriority.UserInput) {
-                    while (event !is DragStopped && event !is DragCancelled) {
-                        (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
-                        event = channel.receive()
-                    }
-                }
-                with(dragLogic) {
-                    if (event is DragStopped) {
-                        processDragStop(event as DragStopped)
-                    } else if (event is DragCancelled) {
-                        processDragCancel()
-                    }
-                }
-            } catch (c: CancellationException) {
-                with(dragLogic) { processDragCancel() }
-            }
-        }
-    }
-    Modifier.pointerInput(state, orientation, enabled, reverseDirection) {
-        if (!enabled) return@pointerInput
+}
+
+internal class DraggableNode(
+    private var state: DraggableState,
+    private var canDrag: (PointerInputChange) -> Boolean,
+    private var orientation: Orientation,
+    private var enabled: Boolean,
+    private var interactionSource: MutableInteractionSource?,
+    private var startDragImmediately: () -> Boolean,
+    private var onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+    private var onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+    private var reverseDirection: Boolean
+) : DelegatingNode(), PointerInputModifierNode {
+    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
+        // TODO: conditionally undelegate when aosp/2462416 lands?
+        if (!enabled) return@SuspendingPointerInputModifierNode
         coroutineScope {
             try {
                 awaitPointerEventScope {
                     while (isActive) {
                         val velocityTracker = VelocityTracker()
+                        @Suppress("UnnecessaryLambdaCreation")
                         awaitDownAndSlop(
-                            canDragState,
-                            startImmediatelyState,
+                            // Use lambdas here to make sure that if these properties are updated
+                            // while we suspend, we point to the new reference when we invoke them.
+                            { canDrag(it) },
+                            { startDragImmediately() },
                             velocityTracker,
                             orientation
                         )?.let {
@@ -303,20 +339,148 @@
                 }
             }
         }
+    })
+
+    private val channel = Channel<DragEvent>(capacity = Channel.UNLIMITED)
+    private var observeChannelJob: Job? = null
+    private var dragInteraction: DragInteraction.Start? = null
+
+    override fun onAttach() {
+        observeChannel()
+    }
+
+    override fun onDetach() {
+        disposeInteractionSource()
+    }
+
+    override fun onPointerEvent(
+        pointerEvent: PointerEvent,
+        pass: PointerEventPass,
+        bounds: IntSize
+    ) {
+        pointerInputNode.onPointerEvent(pointerEvent, pass, bounds)
+    }
+
+    override fun onCancelPointerInput() {
+        pointerInputNode.onCancelPointerInput()
+    }
+
+    fun update(
+        state: DraggableState,
+        canDrag: (PointerInputChange) -> Boolean,
+        orientation: Orientation,
+        enabled: Boolean,
+        interactionSource: MutableInteractionSource?,
+        startDragImmediately: () -> Boolean,
+        onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+        onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+        reverseDirection: Boolean
+    ) {
+        var resetPointerInputHandling = false
+        if (this.state != state) {
+            // Reset observation when the state changes
+            observeChannel()
+            this.state = state
+        }
+        this.canDrag = canDrag
+        if (this.orientation != orientation) {
+            this.orientation = orientation
+            resetPointerInputHandling = true
+        }
+        if (this.enabled != enabled) {
+            this.enabled = enabled
+            if (!enabled) {
+                disposeInteractionSource()
+            }
+            resetPointerInputHandling = true
+        }
+        if (this.interactionSource != interactionSource) {
+            disposeInteractionSource()
+            this.interactionSource = interactionSource
+        }
+        this.startDragImmediately = startDragImmediately
+        this.onDragStarted = onDragStarted
+        this.onDragStopped = onDragStopped
+        if (this.reverseDirection != reverseDirection) {
+            this.reverseDirection = reverseDirection
+            resetPointerInputHandling = true
+        }
+        if (resetPointerInputHandling) {
+            pointerInputNode.resetPointerInputHandler()
+        }
+    }
+
+    private fun observeChannel() {
+        observeChannelJob?.cancel()
+        observeChannelJob = coroutineScope.launch {
+            while (isActive) {
+                var event = channel.receive()
+                if (event !is DragStarted) continue
+                processDragStart(event)
+                try {
+                    state.drag(MutatePriority.UserInput) {
+                        while (event !is DragStopped && event !is DragCancelled) {
+                            (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
+                            event = channel.receive()
+                        }
+                    }
+                    if (event is DragStopped) {
+                        processDragStop(event as DragStopped)
+                    } else if (event is DragCancelled) {
+                        processDragCancel()
+                    }
+                } catch (c: CancellationException) {
+                    processDragCancel()
+                }
+            }
+        }
+    }
+
+    private suspend fun CoroutineScope.processDragStart(event: DragStarted) {
+        dragInteraction?.let { oldInteraction ->
+            interactionSource?.emit(DragInteraction.Cancel(oldInteraction))
+        }
+        val interaction = DragInteraction.Start()
+        interactionSource?.emit(interaction)
+        dragInteraction = interaction
+        onDragStarted.invoke(this, event.startPoint)
+    }
+
+    private suspend fun CoroutineScope.processDragStop(event: DragStopped) {
+        dragInteraction?.let { interaction ->
+            interactionSource?.emit(DragInteraction.Stop(interaction))
+            dragInteraction = null
+        }
+        onDragStopped.invoke(this, event.velocity)
+    }
+
+    private suspend fun CoroutineScope.processDragCancel() {
+        dragInteraction?.let { interaction ->
+            interactionSource?.emit(DragInteraction.Cancel(interaction))
+            dragInteraction = null
+        }
+        onDragStopped.invoke(this, Velocity.Zero)
+    }
+
+    private fun disposeInteractionSource() {
+        dragInteraction?.let { interaction ->
+            interactionSource?.tryEmit(DragInteraction.Cancel(interaction))
+            dragInteraction = null
+        }
     }
 }
 
 private suspend fun AwaitPointerEventScope.awaitDownAndSlop(
-    canDrag: State<(PointerInputChange) -> Boolean>,
-    startDragImmediately: State<() -> Boolean>,
+    canDrag: (PointerInputChange) -> Boolean,
+    startDragImmediately: () -> Boolean,
     velocityTracker: VelocityTracker,
     orientation: Orientation
 ): Pair<PointerInputChange, Offset>? {
     val initialDown =
         awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
-    return if (!canDrag.value.invoke(initialDown)) {
+    return if (!canDrag(initialDown)) {
         null
-    } else if (startDragImmediately.value.invoke()) {
+    } else if (startDragImmediately()) {
         initialDown.consume()
         velocityTracker.addPointerInputChange(initialDown)
         // since we start immediately we don't wait for slop and the initial delta is 0
@@ -392,40 +556,6 @@
     )?.let(onDrag) != null
 }
 
-private class DragLogic(
-    val onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
-    val onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
-    val dragStartInteraction: MutableState<DragInteraction.Start?>,
-    val interactionSource: MutableInteractionSource?
-) {
-
-    suspend fun CoroutineScope.processDragStart(event: DragStarted) {
-        dragStartInteraction.value?.let { oldInteraction ->
-            interactionSource?.emit(DragInteraction.Cancel(oldInteraction))
-        }
-        val interaction = DragInteraction.Start()
-        interactionSource?.emit(interaction)
-        dragStartInteraction.value = interaction
-        onDragStarted.invoke(this, event.startPoint)
-    }
-
-    suspend fun CoroutineScope.processDragStop(event: DragStopped) {
-        dragStartInteraction.value?.let { interaction ->
-            interactionSource?.emit(DragInteraction.Stop(interaction))
-            dragStartInteraction.value = null
-        }
-        onDragStopped.invoke(this, event.velocity)
-    }
-
-    suspend fun CoroutineScope.processDragCancel() {
-        dragStartInteraction.value?.let { interaction ->
-            interactionSource?.emit(DragInteraction.Cancel(interaction))
-            dragStartInteraction.value = null
-        }
-        onDragStopped.invoke(this, Velocity.Zero)
-    }
-}
-
 private class DefaultDraggableState(val onDelta: (Float) -> Unit) : DraggableState {
 
     private val dragScope: DragScope = object : DragScope {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index ccc1466..c387135 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -64,6 +64,7 @@
 import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -271,24 +272,29 @@
     val draggableState = remember { ScrollDraggableState(scrollLogic) }
     val scrollConfig = platformScrollConfig()
 
-    return draggable(
-        draggableState,
-        orientation = orientation,
-        enabled = enabled,
-        interactionSource = interactionSource,
-        reverseDirection = false,
-        startDragImmediately = { scrollLogic.value.shouldScrollImmediately() },
-        onDragStopped = { velocity ->
-            nestedScrollDispatcher.value.coroutineScope.launch {
-                scrollLogic.value.onDragStopped(velocity)
-            }
-        },
-        canDrag = { down -> down.type != PointerType.Mouse }
-    )
+    return this
+        .then(DraggableElement(
+            state = draggableState,
+            orientation = orientation,
+            enabled = enabled,
+            interactionSource = interactionSource,
+            reverseDirection = false,
+            startDragImmediately = { scrollLogic.value.shouldScrollImmediately() },
+            onDragStarted = NoOpOnDragStarted,
+            onDragStopped = { velocity ->
+                nestedScrollDispatcher.value.coroutineScope.launch {
+                    scrollLogic.value.onDragStopped(velocity)
+                }
+            },
+            canDrag = { down -> down.type != PointerType.Mouse }
+        ))
         .then(MouseWheelScrollElement(scrollLogic, scrollConfig))
         .nestedScroll(nestedScrollConnection, nestedScrollDispatcher.value)
 }
 
+// {} isn't being memoized for us, so extract this to make sure we compare equally on recomposition.
+private val NoOpOnDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}
+
 private class MouseWheelScrollElement(
     val scrollingLogicState: State<ScrollingLogic>,
     val mouseWheelScrollConfig: ScrollConfig
@@ -297,9 +303,9 @@
         return MouseWheelScrollNode(scrollingLogicState, mouseWheelScrollConfig)
     }
 
-    override fun update(node: MouseWheelScrollNode): MouseWheelScrollNode = node.also {
-        it.scrollingLogicState = scrollingLogicState
-        it.mouseWheelScrollConfig = mouseWheelScrollConfig
+    override fun update(node: MouseWheelScrollNode) {
+        node.scrollingLogicState = scrollingLogicState
+        node.mouseWheelScrollConfig = mouseWheelScrollConfig
     }
 
     override fun hashCode(): Int {
@@ -326,7 +332,7 @@
     var mouseWheelScrollConfig: ScrollConfig
 ) : DelegatingNode(), PointerInputModifierNode {
 
-    private val pointerInputNode = SuspendingPointerInputModifierNode {
+    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
         awaitPointerEventScope {
             while (true) {
                 val event = awaitScrollEvent()
@@ -344,10 +350,7 @@
                 }
             }
         }
-    }
-        // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
-        //  block
-        .also { delegated { it.node } }
+    })
 
     override fun onPointerEvent(
         pointerEvent: PointerEvent,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
similarity index 60%
rename from compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnapLayoutInfoProvider.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
index e649842..515dfc3 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyGridSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
@@ -14,33 +14,55 @@
  * limitations under the License.
  */
 
-package androidx.compose.foundation.demos.snapping
+package androidx.compose.foundation.gestures.snapping
 
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.calculateTargetValue
+import androidx.compose.animation.splineBasedDecay
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.fastFilter
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
 import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
 import androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastSumBy
+import kotlin.math.absoluteValue
 import kotlin.math.sign
-import kotlin.math.abs
 
-@OptIn(ExperimentalFoundationApi::class)
+/**
+ * A [SnapLayoutInfoProvider] for LazyGrids.
+ *
+ * @param lazyGridState The [LazyGridState] with information about the current state of the grid
+ * @param positionInLayout The desired positioning of the snapped item within the main layout.
+ * This position should be considered with regards to the start edge of the item and the placement
+ * within the viewport.
+ *
+ * @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
+ */
+@ExperimentalFoundationApi
 fun SnapLayoutInfoProvider(
     lazyGridState: LazyGridState,
-    positionInLayout: (Float, Float) -> Float = { mainAxisLayoutSize, mainAxisItemSize ->
-        mainAxisLayoutSize / 2f - mainAxisItemSize / 2f
-    }
+    positionInLayout: SnapPositionInLayout = SnapPositionInLayout.CenterToCenter
 ) = object : SnapLayoutInfoProvider {
     private val layoutInfo: LazyGridLayoutInfo
         get() = lazyGridState.layoutInfo
 
-    override fun Density.calculateApproachOffset(initialVelocity: Float) = 0f
+    override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
+        val decayAnimationSpec: DecayAnimationSpec<Float> = splineBasedDecay(this)
+        val offset =
+            decayAnimationSpec.calculateTargetValue(NoDistance, initialVelocity).absoluteValue
+        val finalDecayOffset = (offset - calculateSnapStepSize()).coerceAtLeast(0f)
+        return if (finalDecayOffset == 0f) {
+            finalDecayOffset
+        } else {
+            finalDecayOffset * initialVelocity.sign
+        }
+    }
 
-    // use the first row/column as a baseline for snapping.
     private val singleAxisItems: List<LazyGridItemInfo>
-        get() = lazyGridState.layoutInfo.visibleItemsInfo.filter {
+        get() = lazyGridState.layoutInfo.visibleItemsInfo.fastFilter {
             if (lazyGridState.layoutInfo.orientation == Orientation.Horizontal) {
                 it.row == 0
             } else {
@@ -54,7 +76,7 @@
         var distanceFromItemBeforeTarget = Float.NEGATIVE_INFINITY
         var distanceFromItemAfterTarget = Float.POSITIVE_INFINITY
 
-        layoutInfo.visibleItemsInfo.forEach { item ->
+        layoutInfo.visibleItemsInfo.fastForEach { item ->
             val distance =
                 calculateDistanceToDesiredSnapPosition(layoutInfo, item, positionInLayout)
 
@@ -79,9 +101,9 @@
     override fun Density.calculateSnapStepSize(): Float {
         return if (singleAxisItems.isNotEmpty()) {
             val size = if (layoutInfo.orientation == Orientation.Vertical) {
-                singleAxisItems.sumOf { it.size.height }
+                singleAxisItems.fastSumBy { it.size.height }
             } else {
-                singleAxisItems.sumOf { it.size.width }
+                singleAxisItems.fastSumBy { it.size.width }
             }
             size / singleAxisItems.size.toFloat()
         } else {
@@ -90,68 +112,43 @@
     }
 }
 
-internal fun calculateDistanceToDesiredSnapPosition(
+@OptIn(ExperimentalFoundationApi::class)
+internal fun Density.calculateDistanceToDesiredSnapPosition(
     layoutInfo: LazyGridLayoutInfo,
     item: LazyGridItemInfo,
-    positionInLayout: (layoutSize: Float, itemSize: Float) -> Float
+    positionInLayout: SnapPositionInLayout = SnapPositionInLayout.CenterToCenter
 ): Float {
 
     val containerSize =
         with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
 
-    val desiredDistance =
-        positionInLayout(containerSize, item.sizeOnMainAxis(layoutInfo.orientation))
+    val desiredDistance = with(positionInLayout) {
+        position(containerSize, item.sizeOnMainAxis(layoutInfo.orientation), item.index)
+    }
 
     val itemCurrentPosition = item.offsetOnMainAxis(layoutInfo.orientation)
-    return itemCurrentPosition - desiredDistance
+    return itemCurrentPosition - desiredDistance.toFloat()
 }
 
-private val LazyGridLayoutInfo.singleAxisViewportSize: Float
+private val LazyGridLayoutInfo.singleAxisViewportSize: Int
     get() = if (orientation == Orientation.Vertical) {
-        viewportSize.height.toFloat()
+        viewportSize.height
     } else {
-        viewportSize.width.toFloat()
+        viewportSize.width
     }
 
-private fun LazyGridItemInfo.sizeOnMainAxis(orientation: Orientation): Float {
+private fun LazyGridItemInfo.sizeOnMainAxis(orientation: Orientation): Int {
     return if (orientation == Orientation.Vertical) {
-        size.height.toFloat()
+        size.height
     } else {
-        size.width.toFloat()
+        size.width
     }
 }
 
-private fun LazyGridItemInfo.offsetOnMainAxis(orientation: Orientation): Float {
+private fun LazyGridItemInfo.offsetOnMainAxis(orientation: Orientation): Int {
     return if (orientation == Orientation.Vertical) {
-        offset.y.toFloat()
+        offset.y
     } else {
-        offset.x.toFloat()
-    }
-}
-
-internal fun calculateFinalOffset(velocity: Float, lowerBound: Float, upperBound: Float): Float {
-
-    fun Float.isValidDistance(): Boolean {
-        return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
-    }
-
-    val finalDistance = when (sign(velocity)) {
-        0f -> {
-            if (abs(upperBound) <= abs(lowerBound)) {
-                upperBound
-            } else {
-                lowerBound
-            }
-        }
-
-        1f -> upperBound
-        -1f -> lowerBound
-        else -> 0f
-    }
-
-    return if (finalDistance.isValidDistance()) {
-        finalDistance
-    } else {
-        0f
+        offset.x
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 093ec01..fece172 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -46,8 +46,7 @@
 @ExperimentalFoundationApi
 fun SnapLayoutInfoProvider(
     lazyListState: LazyListState,
-    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float =
-        { layoutSize, itemSize -> (layoutSize / 2f - itemSize / 2f) }
+    positionInLayout: SnapPositionInLayout = SnapPositionInLayout.CenterToCenter
 ): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider {
 
     private val layoutInfo: LazyListLayoutInfo
@@ -111,16 +110,18 @@
     return rememberSnapFlingBehavior(snappingLayout)
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 internal fun Density.calculateDistanceToDesiredSnapPosition(
     layoutInfo: LazyListLayoutInfo,
     item: LazyListItemInfo,
-    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float
+    positionInLayout: SnapPositionInLayout
 ): Float {
     val containerSize =
         with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
 
-    val desiredDistance =
-        positionInLayout(containerSize.toFloat(), item.size.toFloat())
+    val desiredDistance = with(positionInLayout) {
+        position(containerSize, item.size, item.index)
+    }.toFloat()
 
     val itemCurrentPosition = item.offset
     return itemCurrentPosition - desiredDistance
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.kt
new file mode 100644
index 0000000..3b3f94f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.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.compose.foundation.gestures.snapping
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.ui.unit.Density
+
+/**
+ * Describes the general positioning of a given snap item in its containing layout.
+ */
+@ExperimentalFoundationApi
+fun interface SnapPositionInLayout {
+    /**
+     * Calculates an offset positioning between a container and an element within this container.
+     * The offset calculation is the necessary diff that should be applied to the item offset to
+     * align the item with a position within the container. As a base line, if we wanted to align
+     * the start of the container and the start of the item, we would return 0 in this function.
+     */
+    fun Density.position(layoutSize: Int, itemSize: Int, itemIndex: Int): Int
+
+    companion object {
+        /**
+         * Aligns the center of the item with the center of the containing layout.
+         */
+        val CenterToCenter =
+            SnapPositionInLayout { layoutSize, itemSize, _ -> layoutSize / 2 - itemSize / 2 }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
index 46772c3..16639c9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
@@ -89,10 +89,10 @@
         )
     }
 
-    override fun update(node: ParentSizeNode): ParentSizeNode = node.also {
-        it.fraction = fraction
-        it.widthState = widthState
-        it.heightState = heightState
+    override fun update(node: ParentSizeNode) {
+        node.fraction = fraction
+        node.widthState = widthState
+        node.heightState = heightState
     }
 
     override fun equals(other: Any?): Boolean {
@@ -160,8 +160,8 @@
 
     override fun create(): AnimateItemPlacementNode = AnimateItemPlacementNode(animationSpec)
 
-    override fun update(node: AnimateItemPlacementNode): AnimateItemPlacementNode = node.also {
-        it.delegatingNode.placementAnimationSpec = animationSpec
+    override fun update(node: AnimateItemPlacementNode) {
+        node.delegatingNode.placementAnimationSpec = animationSpec
     }
 
     override fun equals(other: Any?): Boolean {
@@ -184,7 +184,7 @@
     animationSpec: FiniteAnimationSpec<IntOffset>
 ) : DelegatingNode(), ParentDataModifierNode {
 
-    val delegatingNode = delegated { LazyLayoutAnimateItemModifierNode(animationSpec) }
+    val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
 
     override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
 }
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 e606387..e761a7d 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
@@ -29,7 +29,7 @@
 import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.lazy.layout.LazyLayout
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.findIndexByKey
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
 import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
 import androidx.compose.foundation.overscroll
 import androidx.compose.runtime.Composable
@@ -44,8 +44,6 @@
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.offset
-import androidx.compose.ui.util.fastForEach
-import kotlin.math.min
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
@@ -80,12 +78,10 @@
     val itemProvider = rememberLazyListItemProvider(state, content)
 
     val semanticState = rememberLazyListSemanticState(state, isVertical)
-    val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
 
     val measurePolicy = rememberLazyListMeasurePolicy(
         itemProvider,
         state,
-        beyondBoundsInfo,
         contentPadding,
         reverseLayout,
         isVertical,
@@ -114,7 +110,6 @@
             .clipScrollableContainer(orientation)
             .lazyListBeyondBoundsModifier(
                 state,
-                beyondBoundsInfo,
                 beyondBoundsItemCount,
                 reverseLayout,
                 orientation
@@ -158,8 +153,6 @@
     itemProvider: LazyListItemProvider,
     /** The state of the list. */
     state: LazyListState,
-    /** Keeps track of the number of items we measure and place that are beyond visible bounds. */
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
     /** The inner padding to be added for the whole content(nor for each individual item) */
     contentPadding: PaddingValues,
     /** reverse the direction of scrolling and layout */
@@ -178,7 +171,6 @@
     verticalArrangement: Arrangement.Vertical? = null,
 ) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
     state,
-    beyondBoundsInfo,
     contentPadding,
     reverseLayout,
     isVertical,
@@ -295,26 +287,10 @@
             firstVisibleScrollOffset = state.firstVisibleItemScrollOffset
         }
 
-        val pinnedItems = if (!beyondBoundsInfo.hasIntervals() && state.pinnedItems.isEmpty()) {
-            emptyList()
-        } else {
-            val pinnedItems = mutableListOf<Int>()
-            val beyondBoundsRange = if (beyondBoundsInfo.hasIntervals()) {
-                beyondBoundsInfo.start..min(beyondBoundsInfo.end, itemsCount - 1)
-            } else {
-                IntRange.EMPTY
-            }
-            state.pinnedItems.fastForEach {
-                val index = itemProvider.findIndexByKey(it.key, it.index)
-                if (index in beyondBoundsRange) return@fastForEach
-                if (index !in 0 until itemsCount) return@fastForEach
-                pinnedItems.add(index)
-            }
-            for (i in beyondBoundsRange) {
-                pinnedItems.add(i)
-            }
-            pinnedItems
-        }
+        val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+            pinnedItemList = state.pinnedItems,
+            beyondBoundsInfo = state.beyondBoundsInfo
+        )
 
         measureLazyList(
             itemsCount = itemsCount,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
index c048b9b..24e8bd2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
@@ -50,8 +50,10 @@
     }
 
     override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
-        val visibleItems = state.layoutInfo.visibleItemsInfo
-        val averageSize = visibleItems.fastSumBy { it.size } / visibleItems.size
+        val layoutInfo = state.layoutInfo
+        val visibleItems = layoutInfo.visibleItemsInfo
+        val averageSize =
+            visibleItems.fastSumBy { it.size } / visibleItems.size + layoutInfo.mainAxisItemSpacing
         val indexesDiff = index - firstVisibleItemIndex
         var coercedOffset = minOf(abs(targetScrollOffset), averageSize)
         if (targetScrollOffset < 0) coercedOffset *= -1
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
index c52f75b..6def944 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
@@ -17,7 +17,7 @@
 package androidx.compose.foundation.lazy
 
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.layout.BeyondBoundsState
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -32,7 +32,6 @@
 @Composable
 internal fun Modifier.lazyListBeyondBoundsModifier(
     state: LazyListState,
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
     beyondBoundsItemCount: Int,
     reverseLayout: Boolean,
     orientation: Orientation
@@ -41,6 +40,7 @@
     val beyondBoundsState = remember(state, beyondBoundsItemCount) {
         LazyListBeyondBoundsState(state, beyondBoundsItemCount)
     }
+    val beyondBoundsInfo = state.beyondBoundsInfo
     return this then remember(
         beyondBoundsState,
         beyondBoundsInfo,
@@ -61,7 +61,7 @@
 internal class LazyListBeyondBoundsState(
     val state: LazyListState,
     val beyondBoundsItemCount: Int
-) : BeyondBoundsState {
+) : LazyLayoutBeyondBoundsState {
 
     override fun remeasure() {
         state.remeasurement?.forceRemeasure()
@@ -71,9 +71,9 @@
         get() = state.layoutInfo.totalItemsCount
     override val hasVisibleItems: Boolean
         get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
-    override val firstVisibleIndex: Int
+    override val firstPlacedIndex: Int
         get() = maxOf(0, state.firstVisibleItemIndex - beyondBoundsItemCount)
-    override val lastVisibleIndex: Int
+    override val lastPlacedIndex: Int
         get() = minOf(
             itemCount - 1,
             state.layoutInfo.visibleItemsInfo.last().index + beyondBoundsItemCount
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 6e1f924..15ebac4 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
@@ -102,11 +102,9 @@
                         )
                     }
                 } else {
-                    repeat(item.placeablesCount) { placeableIndex ->
-                        item.getParentData(placeableIndex).node?.apply {
-                            if (rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
-                                rawOffset += scrollOffset
-                            }
+                    item.forEachNode { _, node ->
+                        if (node.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+                            node.rawOffset += scrollOffset
                         }
                     }
                     startAnimationsIfNeeded(item)
@@ -117,19 +115,19 @@
             }
         }
 
-        var currentMainAxisOffset = 0
+        var accumulatedOffset = 0
         movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
         movingInFromStartBound.fastForEach { item ->
-            val mainAxisOffset = 0 - currentMainAxisOffset - item.size
-            currentMainAxisOffset += item.size
+            accumulatedOffset += item.size
+            val mainAxisOffset = 0 - accumulatedOffset
             initializeNode(item, mainAxisOffset)
             startAnimationsIfNeeded(item)
         }
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
         movingInFromEndBound.fastForEach { item ->
-            val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
-            currentMainAxisOffset += item.size
+            val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
+            accumulatedOffset += item.size
             initializeNode(item, mainAxisOffset)
             startAnimationsIfNeeded(item)
         }
@@ -163,21 +161,21 @@
             }
         }
 
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
         movingAwayToStartBound.fastForEach { item ->
-            val mainAxisOffset = 0 - currentMainAxisOffset - item.size
-            currentMainAxisOffset += item.size
+            accumulatedOffset += item.size
+            val mainAxisOffset = 0 - accumulatedOffset
 
             val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
             positionedItems.add(positionedItem)
             startAnimationsIfNeeded(positionedItem)
         }
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
         movingAwayToEndBound.fastForEach { item ->
-            val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
-            currentMainAxisOffset += item.size
+            val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
+            accumulatedOffset += item.size
 
             val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
             positionedItems.add(positionedItem)
@@ -214,29 +212,23 @@
         }
 
         // initialize offsets
-        repeat(item.placeablesCount) { placeableIndex ->
-            val node = item.getParentData(placeableIndex).node
-            if (node != null) {
-                val diffToFirstPlaceableOffset =
-                    item.getOffset(placeableIndex) - firstPlaceableOffset
-                node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
-            }
+        item.forEachNode { placeableIndex, node ->
+            val diffToFirstPlaceableOffset =
+                item.getOffset(placeableIndex) - firstPlaceableOffset
+            node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
         }
     }
 
     private fun startAnimationsIfNeeded(item: LazyListPositionedItem) {
-        repeat(item.placeablesCount) { placeableIndex ->
-            val node = item.getParentData(placeableIndex).node
-            if (node != null) {
-                val newTarget = item.getOffset(placeableIndex)
-                val currentTarget = node.rawOffset
-                if (currentTarget == LazyLayoutAnimateItemModifierNode.NotInitialized) {
-                    node.rawOffset = newTarget
-                } else if (currentTarget != newTarget) {
-                    node.rawOffset = newTarget
-                    node.animatePlacementDelta(newTarget - currentTarget)
-                }
+        item.forEachNode { placeableIndex, node ->
+            val newTarget = item.getOffset(placeableIndex)
+            val currentTarget = node.rawOffset
+            if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+                currentTarget != newTarget
+            ) {
+                node.animatePlacementDelta(newTarget - currentTarget)
             }
+            node.rawOffset = newTarget
         }
     }
 
@@ -244,11 +236,15 @@
 
     private val LazyListPositionedItem.hasAnimations: Boolean
         get() {
-            repeat(placeablesCount) { index ->
-                if (getParentData(index).node != null) {
-                    return true
-                }
-            }
+            forEachNode { _, _ -> return true }
             return false
         }
+
+    private inline fun LazyListPositionedItem.forEachNode(
+        block: (placeableIndex: Int, node: LazyLayoutAnimateItemModifierNode) -> Unit
+    ) {
+        repeat(placeablesCount) { index ->
+            getParentData(index).node?.let { block(index, it) }
+        }
+    }
 }
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 f3d19f1b..760f9fa 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,6 +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.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
 import androidx.compose.foundation.lazy.layout.animateScrollToItem
@@ -216,6 +217,8 @@
 
     internal val placementAnimator = LazyListItemPlacementAnimator()
 
+    internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
     /**
      * Constraints passed to the prefetcher for premeasuring the prefetched items.
      */
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 ec71a7b..b60f6ed 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
@@ -29,6 +29,7 @@
 import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.lazy.layout.LazyLayout
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
 import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
 import androidx.compose.foundation.overscroll
 import androidx.compose.runtime.Composable
@@ -86,7 +87,7 @@
         reverseLayout,
         isVertical,
         horizontalArrangement,
-        verticalArrangement
+        verticalArrangement,
     )
 
     state.isVertical = isVertical
@@ -106,6 +107,11 @@
                 reverseScrolling = reverseLayout
             )
             .clipScrollableContainer(orientation)
+            .lazyGridBeyondBoundsModifier(
+                state,
+                reverseLayout,
+                orientation
+            )
             .overscroll(overscrollEffect)
             .scrollable(
                 orientation = orientation,
@@ -170,7 +176,7 @@
     reverseLayout,
     isVertical,
     horizontalArrangement,
-    verticalArrangement
+    verticalArrangement,
 ) {
     { containerConstraints ->
         checkScrollableContainerConstraints(
@@ -314,9 +320,14 @@
                 firstVisibleLineScrollOffset = 0
             }
         }
+
+        val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+            state.pinnedItems,
+            state.beyondBoundsInfo
+        )
+
         measureLazyGrid(
             itemsCount = itemsCount,
-            itemProvider = itemProvider,
             measuredLineProvider = measuredLineProvider,
             measuredItemProvider = measuredItemProvider,
             mainAxisAvailableSize = mainAxisAvailableSize,
@@ -334,7 +345,7 @@
             density = this,
             placementAnimator = state.placementAnimator,
             spanLayoutProvider = spanLayoutProvider,
-            pinnedItems = state.pinnedItems,
+            pinnedItems = pinnedItems,
             layout = { width, height, placement ->
                 layout(
                     containerConstraints.constrainWidth(width + totalHorizontalPadding),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
index 174036d..b29a04f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
@@ -54,10 +54,9 @@
     }
 
     override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
-        val visibleItems = state.layoutInfo.visibleItemsInfo
         val slotsPerLine = state.slotsPerLine
         val averageLineMainAxisSize = calculateLineAverageMainAxisSize(
-            visibleItems,
+            state.layoutInfo,
             state.isVertical
         )
         val before = index < firstVisibleItemIndex
@@ -74,9 +73,10 @@
     override val numOfItemsForTeleport: Int get() = 100 * state.slotsPerLine
 
     private fun calculateLineAverageMainAxisSize(
-        visibleItems: List<LazyGridItemInfo>,
+        layoutInfo: LazyGridLayoutInfo,
         isVertical: Boolean
     ): Int {
+        val visibleItems = layoutInfo.visibleItemsInfo
         val lineOf: (Int) -> Int = {
             if (isVertical) visibleItems[it].row else visibleItems[it].column
         }
@@ -113,7 +113,7 @@
             lineStartIndex = lineEndIndex
         }
 
-        return totalLinesMainAxisSize / linesCount
+        return totalLinesMainAxisSize / linesCount + layoutInfo.mainAxisItemSpacing
     }
 
     override suspend fun scroll(block: suspend ScrollScope.() -> Unit) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt
new file mode 100644
index 0000000..816d08d
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.grid
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+
+/**
+ * This modifier is used to measure and place additional items when the lazyList receives a
+ * request to layout items beyond the visible bounds.
+ */
+@Suppress("ComposableModifierFactory")
+@Composable
+internal fun Modifier.lazyGridBeyondBoundsModifier(
+    state: LazyGridState,
+    reverseLayout: Boolean,
+    orientation: Orientation
+): Modifier {
+    val layoutDirection = LocalLayoutDirection.current
+    val beyondBoundsState = remember(state) {
+        LazyGridBeyondBoundsState(state)
+    }
+    return this then remember(
+        state,
+        beyondBoundsState,
+        reverseLayout,
+        layoutDirection,
+        orientation
+    ) {
+        LazyLayoutBeyondBoundsModifierLocal(
+            beyondBoundsState,
+            state.beyondBoundsInfo,
+            reverseLayout,
+            layoutDirection,
+            orientation
+        )
+    }
+}
+
+internal class LazyGridBeyondBoundsState(
+    val state: LazyGridState,
+) : LazyLayoutBeyondBoundsState {
+
+    override fun remeasure() {
+        state.remeasurement?.forceRemeasure()
+    }
+
+    override val itemCount: Int
+        get() = state.layoutInfo.totalItemsCount
+    override val hasVisibleItems: Boolean
+        get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
+    override val firstPlacedIndex: Int
+        get() = state.firstVisibleItemIndex
+    override val lastPlacedIndex: Int
+        get() = state.layoutInfo.visibleItemsInfo.last().index
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
index 5492a12..f54d52e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation.lazy.grid
 
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.ScrollableDefaults
 import androidx.compose.foundation.layout.Arrangement
@@ -327,7 +326,6 @@
      * If the screen is 88.dp wide tne there will be 4 columns 20.dp each with remaining 8.dp
      * distributed through [Arrangement.Horizontal].
      */
-    @ExperimentalFoundationApi
     class FixedSize(private val size: Dp) : GridCells {
         init {
             require(size > 0.dp) { "Provided size $size should be larger than zero." }
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 5bdc836..2f36dec 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
@@ -107,11 +107,9 @@
                         )
                     }
                 } else {
-                    repeat(item.placeablesCount) { placeableIndex ->
-                        item.getParentData(placeableIndex).node?.apply {
-                            if (rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
-                                rawOffset += scrollOffset
-                            }
+                    item.forEachNode {
+                        if (it.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+                            it.rawOffset += scrollOffset
                         }
                     }
                     itemInfo.crossAxisSize = item.getCrossAxisSize()
@@ -124,7 +122,7 @@
             }
         }
 
-        var currentMainAxisOffset = 0
+        var accumulatedOffset = 0
         var previousLine = -1
         var previousLineMainAxisSize = 0
         movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
@@ -133,15 +131,15 @@
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
             } else {
-                currentMainAxisOffset += previousLineMainAxisSize
+                accumulatedOffset += previousLineMainAxisSize
                 previousLineMainAxisSize = item.getMainAxisSize()
                 previousLine = line
             }
-            val mainAxisOffset = 0 - currentMainAxisOffset - item.getMainAxisSize()
+            val mainAxisOffset = 0 - accumulatedOffset - item.getMainAxisSize()
             initializeNode(item, mainAxisOffset)
             startAnimationsIfNeeded(item)
         }
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
         movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
@@ -150,11 +148,11 @@
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
             } else {
-                currentMainAxisOffset += previousLineMainAxisSize
+                accumulatedOffset += previousLineMainAxisSize
                 previousLineMainAxisSize = item.getMainAxisSize()
                 previousLine = line
             }
-            val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
+            val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
             initializeNode(item, mainAxisOffset)
             startAnimationsIfNeeded(item)
         }
@@ -196,7 +194,7 @@
             }
         }
 
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
         movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
@@ -205,11 +203,11 @@
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
             } else {
-                currentMainAxisOffset += previousLineMainAxisSize
+                accumulatedOffset += previousLineMainAxisSize
                 previousLineMainAxisSize = item.mainAxisSize
                 previousLine = line
             }
-            val mainAxisOffset = 0 - currentMainAxisOffset - item.mainAxisSize
+            val mainAxisOffset = 0 - accumulatedOffset - item.mainAxisSize
 
             val itemInfo = keyToItemInfoMap.getValue(item.key)
 
@@ -224,7 +222,7 @@
             positionedItems.add(positionedItem)
             startAnimationsIfNeeded(positionedItem)
         }
-        currentMainAxisOffset = 0
+        accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
         movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
@@ -233,11 +231,11 @@
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
             } else {
-                currentMainAxisOffset += previousLineMainAxisSize
+                accumulatedOffset += previousLineMainAxisSize
                 previousLineMainAxisSize = item.mainAxisSize
                 previousLine = line
             }
-            val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
+            val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
 
             val itemInfo = keyToItemInfoMap.getValue(item.key)
             val positionedItem = item.position(
@@ -283,29 +281,23 @@
         }
 
         // initialize offsets
-        repeat(item.placeablesCount) { placeableIndex ->
-            val node = item.getParentData(placeableIndex).node
-            if (node != null) {
-                val diffToFirstPlaceableOffset =
-                    item.offset - firstPlaceableOffset
-                node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
-            }
+        item.forEachNode { node ->
+            val diffToFirstPlaceableOffset =
+                item.offset - firstPlaceableOffset
+            node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
         }
     }
 
     private fun startAnimationsIfNeeded(item: LazyGridPositionedItem) {
-        repeat(item.placeablesCount) { placeableIndex ->
-            val node = item.getParentData(placeableIndex).node
-            if (node != null) {
-                val newTarget = item.offset
-                val currentTarget = node.rawOffset
-                if (currentTarget == LazyLayoutAnimateItemModifierNode.NotInitialized) {
-                    node.rawOffset = item.offset
-                } else if (currentTarget != newTarget) {
-                    node.rawOffset = newTarget
-                    node.animatePlacementDelta(item.offset - currentTarget)
-                }
+        item.forEachNode { node ->
+            val newTarget = item.offset
+            val currentTarget = node.rawOffset
+            if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+                currentTarget != newTarget
+            ) {
+                node.animatePlacementDelta(newTarget - currentTarget)
             }
+            node.rawOffset = newTarget
         }
     }
 
@@ -313,13 +305,17 @@
 
     private val LazyGridPositionedItem.hasAnimations: Boolean
         get() {
-            repeat(placeablesCount) { index ->
-                if (getParentData(index).node != null) {
-                    return true
-                }
-            }
+            forEachNode { return true }
             return false
         }
+
+    private inline fun LazyGridPositionedItem.forEachNode(
+        block: (LazyLayoutAnimateItemModifierNode) -> Unit
+    ) {
+        repeat(placeablesCount) { index ->
+            getParentData(index).node?.let(block)
+        }
+    }
 }
 
 private class ItemInfo(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
index 794b9216..208ee3f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
@@ -36,7 +36,7 @@
      *
      * When you provide a key via [LazyGridScope.item]/[LazyGridScope.items] this modifier will
      * enable item reordering animations. Aside from item reordering all other position changes
-     * caused by events like arrangement or alignment changes will also be animated.
+     * caused by events like arrangement changes will also be animated.
      *
      * @param animationSpec a finite animation that will be used to animate the item placement.
      */
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
index f8dda36..09fb209 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
@@ -40,8 +40,8 @@
 
     override fun create(): AnimateItemPlacementNode = AnimateItemPlacementNode(animationSpec)
 
-    override fun update(node: AnimateItemPlacementNode): AnimateItemPlacementNode = node.also {
-        it.delegatingNode.placementAnimationSpec = animationSpec
+    override fun update(node: AnimateItemPlacementNode) {
+        node.delegatingNode.placementAnimationSpec = animationSpec
     }
 
     override fun equals(other: Any?): Boolean {
@@ -64,7 +64,7 @@
     animationSpec: FiniteAnimationSpec<IntOffset>
 ) : DelegatingNode(), ParentDataModifierNode {
 
-    val delegatingNode = delegated { LazyLayoutAnimateItemModifierNode(animationSpec) }
+    val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
 
     override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
 }
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 b22522a..a2a11fe 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
@@ -20,8 +20,6 @@
 import androidx.compose.foundation.fastFilter
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
-import androidx.compose.foundation.lazy.layout.findIndexByKey
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.unit.Constraints
@@ -43,7 +41,6 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal fun measureLazyGrid(
     itemsCount: Int,
-    itemProvider: LazyGridItemProvider,
     measuredLineProvider: LazyGridMeasuredLineProvider,
     measuredItemProvider: LazyGridMeasuredItemProvider,
     mainAxisAvailableSize: Int,
@@ -61,7 +58,7 @@
     density: Density,
     placementAnimator: LazyGridItemPlacementAnimator,
     spanLayoutProvider: LazyGridSpanLayoutProvider,
-    pinnedItems: LazyLayoutPinnedItemList,
+    pinnedItems: List<Int>,
     layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
 ): LazyGridMeasureResult {
     require(beforeContentPadding >= 0)
@@ -213,7 +210,6 @@
         val extraItemsBefore = calculateExtraItems(
             pinnedItems,
             measuredItemProvider,
-            itemProvider,
             itemConstraints = { measuredLineProvider.itemConstraints(it) },
             filter = { it in 0 until firstItemIndex }
         )
@@ -221,7 +217,6 @@
         val extraItemsAfter = calculateExtraItems(
             pinnedItems,
             measuredItemProvider,
-            itemProvider,
             itemConstraints = { measuredLineProvider.itemConstraints(it) },
             filter = { it in (lastItemIndex + 1) until itemsCount }
         )
@@ -307,16 +302,14 @@
 
 @ExperimentalFoundationApi
 private inline fun calculateExtraItems(
-    pinnedItems: LazyLayoutPinnedItemList,
+    pinnedItems: List<Int>,
     measuredItemProvider: LazyGridMeasuredItemProvider,
-    itemProvider: LazyGridItemProvider,
     itemConstraints: (ItemIndex) -> Constraints,
     filter: (Int) -> Boolean
 ): List<LazyGridMeasuredItem> {
     var items: MutableList<LazyGridMeasuredItem>? = null
 
-    pinnedItems.fastForEach { item ->
-        val index = itemProvider.findIndexByKey(item.key, item.index)
+    pinnedItems.fastForEach { index ->
         if (filter(index)) {
             val itemIndex = ItemIndex(index)
             val constraints = itemConstraints(itemIndex)
@@ -430,6 +423,6 @@
         crossAxisOffset = 0,
         layoutWidth = layoutWidth,
         layoutHeight = layoutHeight,
-        row = 0,
-        column = 0
+        row = -1,
+        column = -1
     )
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 87934a4..2127ee7 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
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.lazy.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.animateScrollToItem
@@ -200,7 +201,7 @@
      * The [Remeasurement] object associated with our layout. It allows us to remeasure
      * synchronously during scroll.
      */
-    private var remeasurement: Remeasurement? by mutableStateOf(null)
+    internal var remeasurement: Remeasurement? by mutableStateOf(null)
 
     /**
      * The modifier which provides [remeasurement].
@@ -225,6 +226,8 @@
 
     internal val placementAnimator = LazyGridItemPlacementAnimator()
 
+    internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
     private val animateScrollScope = LazyGridAnimateScrollScope(this)
 
     /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
index 3548bcd..78fd421 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
@@ -208,7 +208,11 @@
                     // We don't throw ItemFoundInScroll when we snap, because once we've snapped to
                     // the final position, there's no need to animate to it.
                     if (isOvershot()) {
-                        debugLog { "Overshot" }
+                        debugLog {
+                            "Overshot, " +
+                                "item $firstVisibleItemIndex at $firstVisibleItemScrollOffset, " +
+                                "target is $scrollOffset"
+                        }
                         snapToItem(index = index, scrollOffset = scrollOffset)
                         loop = false
                         cancelAnimation()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
similarity index 79%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
index ee872fd..34d2918 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 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.
@@ -14,24 +14,25 @@
  * limitations under the License.
  */
 
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.layout
 
 import androidx.compose.runtime.collection.mutableVectorOf
 
 /**
  * This data structure is used to save information about the number of "beyond bounds items"
- * that we want to compose. These items are not within the visible bounds of the lazylist,
+ * that we want to compose. These items are not within the visible bounds of the lazy layout,
  * but we compose them because they are explicitly requested through the
  * [beyond bounds layout API][androidx.compose.ui.layout.BeyondBoundsLayout].
  *
- * When the LazyList receives a
- * [searchBeyondBounds][androidx.compose.ui.layout.BeyondBoundsLayout.searchBeyondBounds] request to
- * layout items beyond visible bounds, it creates an instance of [LazyListBeyondBoundsInfo] by using
- * the [addInterval] function. This returns the interval of items that are currently composed,
- * and we can edit this interval to control the number of beyond bounds items.
+ * When the lazy layout receives a
+ * [layout][androidx.compose.ui.layout.BeyondBoundsLayout.layout] request to layout items beyond
+ * visible bounds, it creates an instance of [LazyLayoutBeyondBoundsInfo.Interval] by using the
+ * [addInterval] function.
+ * This returns the interval of items that are currently composed, and we can request other
+ * intervals to control the number of beyond bounds items.
  *
- * There can be multiple intervals created at the same time, and LazyList merges all the
- * intervals to calculate the effective beyond bounds items.
+ * There can be multiple intervals created at the same time, and [LazyLayoutBeyondBoundsInfo] merges
+ * all the intervals to calculate the effective beyond bounds items.
  *
  * The [beyond bounds layout API][androidx.compose.ui.layout.BeyondBoundsLayout] is designed to be
  * synchronous, so once you are done using the items, call [removeInterval] to remove
@@ -43,11 +44,10 @@
  *
  * 1. To allow items to be pinned while they are being scrolled into view.
  *
- * 2. To allow users to call
- * [searchBeyondBounds][androidx.compose.ui.layout.BeyondBoundsLayout.searchBeyondBounds]
- * from within the completion block of another searchBeyondBounds call.
+ * 2. To allow users to call [layout][androidx.compose.ui.layout.BeyondBoundsLayout.layout] from
+ * within the completion block of another layout call.
  */
-internal class LazyListBeyondBoundsInfo {
+internal class LazyLayoutBeyondBoundsInfo {
     private val beyondBoundsItems = mutableVectorOf<Interval>()
 
     /**
@@ -108,7 +108,7 @@
         }
 
     /**
-     * The Interval used to implement [LazyListBeyondBoundsInfo].
+     * The Interval used to implement [LazyLayoutBeyondBoundsInfo].
      */
     internal data class Interval(
         /** The start index for the interval. */
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
index 822a91c..854dee2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
@@ -17,8 +17,7 @@
 package androidx.compose.foundation.lazy.layout
 
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo.Interval
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo.Interval
 import androidx.compose.ui.layout.BeyondBoundsLayout
 import androidx.compose.ui.layout.BeyondBoundsLayout.BeyondBoundsScope
 import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
@@ -35,8 +34,8 @@
 import androidx.compose.ui.unit.LayoutDirection.Rtl
 
 internal class LazyLayoutBeyondBoundsModifierLocal(
-    private val state: BeyondBoundsState,
-    private val beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    private val state: LazyLayoutBeyondBoundsState,
+    private val beyondBoundsInfo: LazyLayoutBeyondBoundsInfo,
     private val reverseLayout: Boolean,
     private val layoutDirection: LayoutDirection,
     private val orientation: Orientation
@@ -63,9 +62,9 @@
 
         // We use a new interval each time because this function is re-entrant.
         val startIndex = if (direction.isForward()) {
-            state.lastVisibleIndex
+            state.lastPlacedIndex
         } else {
-            state.firstVisibleIndex
+            state.firstPlacedIndex
         }
         var interval = beyondBoundsInfo.addInterval(startIndex, startIndex)
         var found: T? = null
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt
new file mode 100644
index 0000000..2996035
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.foundation.ExperimentalFoundationApi
+import androidx.compose.ui.util.fastForEach
+import kotlin.math.min
+
+internal interface LazyLayoutBeyondBoundsState {
+
+    fun remeasure()
+
+    val itemCount: Int
+
+    val hasVisibleItems: Boolean
+
+    val firstPlacedIndex: Int
+
+    val lastPlacedIndex: Int
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal fun LazyLayoutItemProvider.calculateLazyLayoutPinnedIndices(
+    pinnedItemList: LazyLayoutPinnedItemList,
+    beyondBoundsInfo: LazyLayoutBeyondBoundsInfo,
+): List<Int> {
+    if (!beyondBoundsInfo.hasIntervals() && pinnedItemList.isEmpty()) {
+        return emptyList()
+    } else {
+        val pinnedItems = mutableListOf<Int>()
+        val beyondBoundsRange = if (beyondBoundsInfo.hasIntervals()) {
+            beyondBoundsInfo.start..min(beyondBoundsInfo.end, itemCount - 1)
+        } else {
+            IntRange.EMPTY
+        }
+        pinnedItemList.fastForEach {
+            val index = findIndexByKey(it.key, it.index)
+            if (index in beyondBoundsRange) return@fastForEach
+            if (index !in 0 until itemCount) return@fastForEach
+            pinnedItems.add(index)
+        }
+        for (i in beyondBoundsRange) {
+            pinnedItems.add(i)
+        }
+        return pinnedItems
+    }
+}
\ No newline at end of file
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 1f8aa80..7faf82e 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
@@ -72,7 +72,7 @@
         orientation,
         mainAxisSpacing,
         crossAxisSpacing,
-        slots
+        slots,
     )
     val semanticState = rememberLazyStaggeredGridSemanticState(state, reverseLayout)
 
@@ -81,7 +81,19 @@
     LazyLayout(
         modifier = modifier
             .then(state.remeasurementModifier)
+            .lazyLayoutSemantics(
+                itemProvider = itemProvider,
+                state = semanticState,
+                orientation = orientation,
+                userScrollEnabled = userScrollEnabled,
+                reverseScrolling = reverseLayout
+            )
             .clipScrollableContainer(orientation)
+            .lazyStaggeredGridBeyondBoundsModifier(
+                state = state,
+                reverseLayout = reverseLayout,
+                orientation = orientation
+            )
             .overscroll(overscrollEffect)
             .scrollable(
                 orientation = orientation,
@@ -95,13 +107,6 @@
                 state = state,
                 overscrollEffect = overscrollEffect,
                 enabled = userScrollEnabled
-            )
-            .lazyLayoutSemantics(
-                itemProvider = itemProvider,
-                state = semanticState,
-                orientation = orientation,
-                userScrollEnabled = userScrollEnabled,
-                reverseScrolling = reverseLayout
             ),
         prefetchState = state.prefetchState,
         itemProvider = itemProvider,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
index 57f75d2..eb7fc8c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
@@ -50,15 +50,18 @@
     }
 
     override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
-        val visibleItems = state.layoutInfo.visibleItemsInfo
-        val itemSizeSum = visibleItems
-            .fastSumBy { if (state.isVertical) it.size.height else it.size.width }
-        val averageMainAxisItemSize = itemSizeSum / (visibleItems.size * state.laneCount)
+        val layoutInfo = state.layoutInfo
+        val visibleItems = layoutInfo.visibleItemsInfo
+        val itemSizeSum = visibleItems.fastSumBy {
+            if (state.isVertical) it.size.height else it.size.width
+        }
+        val averageMainAxisItemSize =
+            itemSizeSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
 
-        val indexesDiff = index - firstVisibleItemIndex
+        val lineDiff = index / state.laneCount - firstVisibleItemIndex / state.laneCount
         var coercedOffset = minOf(abs(targetScrollOffset), averageMainAxisItemSize)
         if (targetScrollOffset < 0) coercedOffset *= -1
-        return (averageMainAxisItemSize * indexesDiff).toFloat() +
+        return averageMainAxisItemSize * lineDiff.toFloat() +
             coercedOffset - firstVisibleItemScrollOffset
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt
new file mode 100644
index 0000000..445098f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.staggeredgrid
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+
+/**
+ * This modifier is used to measure and place additional items when the lazyList receives a
+ * request to layout items beyond the visible bounds.
+ */
+@Suppress("ComposableModifierFactory")
+@Composable
+internal fun Modifier.lazyStaggeredGridBeyondBoundsModifier(
+    state: LazyStaggeredGridState,
+    reverseLayout: Boolean,
+    orientation: Orientation
+): Modifier {
+    val layoutDirection = LocalLayoutDirection.current
+    val beyondBoundsState = remember(state) {
+        LazyStaggeredGridBeyondBoundsState(state)
+    }
+    return this then remember(
+        beyondBoundsState,
+        state,
+        reverseLayout,
+        layoutDirection,
+        orientation
+    ) {
+        LazyLayoutBeyondBoundsModifierLocal(
+            beyondBoundsState,
+            state.beyondBoundsInfo,
+            reverseLayout,
+            layoutDirection,
+            orientation
+        )
+    }
+}
+
+internal class LazyStaggeredGridBeyondBoundsState(
+    val state: LazyStaggeredGridState,
+) : LazyLayoutBeyondBoundsState {
+
+    override fun remeasure() {
+        state.remeasurement?.forceRemeasure()
+    }
+
+    override val itemCount: Int
+        get() = state.layoutInfo.totalItemsCount
+    override val hasVisibleItems: Boolean
+        get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
+    override val firstPlacedIndex: Int
+        get() = state.firstVisibleItemIndex
+    override val lastPlacedIndex: Int
+        get() = state.layoutInfo.visibleItemsInfo.last().index
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
index 8941796..cca7a11 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
@@ -241,11 +241,6 @@
 internal annotation class LazyStaggeredGridScopeMarker
 
 /**
- * Receiver scope for itemContent in [LazyStaggeredGridScope.item]
- */
-sealed interface LazyStaggeredGridItemScope
-
-/**
  * Receiver scope for [LazyVerticalStaggeredGrid] and [LazyHorizontalStaggeredGrid]
  */
 @LazyStaggeredGridScopeMarker
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
index 4db8415..dc7406d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
@@ -69,9 +69,6 @@
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-internal object LazyStaggeredGridItemScopeImpl : LazyStaggeredGridItemScope
-
-@OptIn(ExperimentalFoundationApi::class)
 internal class LazyStaggeredGridInterval(
     override val key: ((index: Int) -> Any)?,
     override val type: ((index: Int) -> Any?),
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
new file mode 100644
index 0000000..11895ed
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
@@ -0,0 +1,280 @@
+/*
+ * 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.staggeredgrid
+
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastForEach
+
+/**
+ * Handles the item placement animations when it is set via
+ * [LazyStaggeredGridItemScope.animateItemPlacement].
+ *
+ * This class is responsible for detecting when item position changed, figuring our start/end
+ * offsets and starting the animations.
+ */
+internal class LazyStaggeredGridItemPlacementAnimator {
+    // state containing relevant info for active items.
+    private val keyToItemInfoMap = mutableMapOf<Any, ItemInfo>()
+
+    // snapshot of the key to index map used for the last measuring.
+    private var keyToIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap
+
+    // keeps the index of the first visible item index.
+    private var firstVisibleIndex = 0
+
+    // stored to not allocate it every pass.
+    private val movingAwayKeys = LinkedHashSet<Any>()
+    private val movingInFromStartBound = mutableListOf<LazyStaggeredGridPositionedItem>()
+    private val movingInFromEndBound = mutableListOf<LazyStaggeredGridPositionedItem>()
+    private val movingAwayToStartBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
+    private val movingAwayToEndBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
+
+    /**
+     * Should be called after the measuring so we can detect position changes and start animations.
+     *
+     * Note that this method can compose new item and add it into the [positionedItems] list.
+     */
+    fun onMeasured(
+        consumedScroll: Int,
+        layoutWidth: Int,
+        layoutHeight: Int,
+        positionedItems: MutableList<LazyStaggeredGridPositionedItem>,
+        itemProvider: LazyStaggeredGridMeasureProvider,
+        isVertical: Boolean,
+        laneCount: Int
+    ) {
+        if (!positionedItems.fastAny { it.hasAnimations } && keyToItemInfoMap.isEmpty()) {
+            // no animations specified - no work needed
+            reset()
+            return
+        }
+
+        val previousFirstVisibleIndex = firstVisibleIndex
+        firstVisibleIndex = positionedItems.firstOrNull()?.index ?: 0
+        val previousKeyToIndexMap = keyToIndexMap
+        keyToIndexMap = itemProvider.keyToIndexMap
+
+        val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
+
+        // the consumed scroll is considered as a delta we don't need to animate
+        val scrollOffset = if (isVertical) {
+            IntOffset(0, consumedScroll)
+        } else {
+            IntOffset(consumedScroll, 0)
+        }
+
+        // first add all items we had in the previous run
+        movingAwayKeys.addAll(keyToItemInfoMap.keys)
+        // iterate through the items which are visible (without animated offsets)
+        positionedItems.fastForEach { item ->
+            // remove items we have in the current one as they are still visible.
+            movingAwayKeys.remove(item.key)
+            if (item.hasAnimations) {
+                val itemInfo = keyToItemInfoMap[item.key]
+                // there is no state associated with this item yet
+                if (itemInfo == null) {
+                    keyToItemInfoMap[item.key] =
+                        ItemInfo(item.lane, item.span, item.crossAxisOffset)
+                    val previousIndex = previousKeyToIndexMap[item.key]
+                    if (previousIndex != -1 && item.index != previousIndex) {
+                        if (previousIndex < previousFirstVisibleIndex) {
+                            // the larger index will be in the start of the list
+                            movingInFromStartBound.add(item)
+                        } else {
+                            movingInFromEndBound.add(item)
+                        }
+                    } else {
+                        initializeNode(
+                            item,
+                            item.offset.let { if (item.isVertical) it.y else it.x }
+                        )
+                    }
+                } else {
+                    item.forEachNode {
+                        if (it.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+                            it.rawOffset += scrollOffset
+                        }
+                    }
+                    itemInfo.lane = item.lane
+                    itemInfo.span = item.span
+                    itemInfo.crossAxisOffset = item.crossAxisOffset
+                    startAnimationsIfNeeded(item)
+                }
+            } else {
+                // no animation, clean up if needed
+                keyToItemInfoMap.remove(item.key)
+            }
+        }
+
+        val accumulatedOffsetPerLane = IntArray(laneCount) { 0 }
+        if (movingInFromStartBound.isNotEmpty()) {
+            movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
+            movingInFromStartBound.fastForEach { item ->
+                accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+                val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
+                initializeNode(item, mainAxisOffset)
+                startAnimationsIfNeeded(item)
+            }
+            accumulatedOffsetPerLane.fill(0)
+        }
+        if (movingInFromEndBound.isNotEmpty()) {
+            movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
+            movingInFromEndBound.fastForEach { item ->
+                val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
+                accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+                initializeNode(item, mainAxisOffset)
+                startAnimationsIfNeeded(item)
+            }
+            accumulatedOffsetPerLane.fill(0)
+        }
+
+        movingAwayKeys.forEach { key ->
+            // 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]
+
+            if (newIndex == -1) {
+                keyToItemInfoMap.remove(key)
+            } else {
+                val item = itemProvider.getAndMeasure(
+                    newIndex,
+                    SpanRange(itemInfo.lane, itemInfo.span)
+                )
+                // check if we have any active placement animation on the item
+                var inProgress = false
+                repeat(item.placeablesCount) {
+                    if (item.getParentData(it).node?.isAnimationInProgress == true) {
+                        inProgress = true
+                        return@repeat
+                    }
+                }
+                if ((!inProgress && newIndex == previousKeyToIndexMap[key])) {
+                    keyToItemInfoMap.remove(key)
+                } else {
+                    if (newIndex < firstVisibleIndex) {
+                        movingAwayToStartBound.add(item)
+                    } else {
+                        movingAwayToEndBound.add(item)
+                    }
+                }
+            }
+        }
+
+        if (movingAwayToStartBound.isNotEmpty()) {
+            movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
+            movingAwayToStartBound.fastForEach { item ->
+                accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+                val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
+
+                val itemInfo = keyToItemInfoMap.getValue(item.key)
+                val positionedItem =
+                    item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+                positionedItems.add(positionedItem)
+                startAnimationsIfNeeded(positionedItem)
+            }
+            accumulatedOffsetPerLane.fill(0)
+        }
+        if (movingAwayToEndBound.isNotEmpty()) {
+            movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
+            movingAwayToEndBound.fastForEach { item ->
+                val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
+                accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+
+                val itemInfo = keyToItemInfoMap.getValue(item.key)
+                val positionedItem =
+                    item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+                positionedItems.add(positionedItem)
+                startAnimationsIfNeeded(positionedItem)
+            }
+        }
+
+        movingInFromStartBound.clear()
+        movingInFromEndBound.clear()
+        movingAwayToStartBound.clear()
+        movingAwayToEndBound.clear()
+        movingAwayKeys.clear()
+    }
+
+    /**
+     * Should be called when the animations are not needed for the next positions change,
+     * for example when we snap to a new position.
+     */
+    fun reset() {
+        keyToItemInfoMap.clear()
+        keyToIndexMap = LazyLayoutKeyIndexMap
+        firstVisibleIndex = -1
+    }
+
+    private fun initializeNode(
+        item: LazyStaggeredGridPositionedItem,
+        mainAxisOffset: Int
+    ) {
+        val firstPlaceableOffset = item.offset
+
+        val targetFirstPlaceableOffset = if (item.isVertical) {
+            firstPlaceableOffset.copy(y = mainAxisOffset)
+        } else {
+            firstPlaceableOffset.copy(x = mainAxisOffset)
+        }
+
+        // initialize offsets
+        item.forEachNode { node ->
+            val diffToFirstPlaceableOffset =
+                item.offset - firstPlaceableOffset
+            node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
+        }
+    }
+
+    private fun startAnimationsIfNeeded(item: LazyStaggeredGridPositionedItem) {
+        item.forEachNode { node ->
+            val newTarget = item.offset
+            val currentTarget = node.rawOffset
+            if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+                currentTarget != newTarget
+            ) {
+                node.animatePlacementDelta(newTarget - currentTarget)
+            }
+            node.rawOffset = newTarget
+        }
+    }
+
+    private val Any?.node get() = this as? LazyLayoutAnimateItemModifierNode
+
+    private val LazyStaggeredGridPositionedItem.hasAnimations: Boolean
+        get() {
+            forEachNode { return true }
+            return false
+        }
+
+    private inline fun LazyStaggeredGridPositionedItem.forEachNode(
+        block: (LazyLayoutAnimateItemModifierNode) -> Unit
+    ) {
+        repeat(placeablesCount) { index ->
+            getParentData(index).node?.let(block)
+        }
+    }
+}
+
+private class ItemInfo(
+    var lane: Int,
+    var span: Int,
+    var crossAxisOffset: Int
+)
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 f9a8f36..b54bd72 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
@@ -31,6 +31,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal interface LazyStaggeredGridItemProvider : LazyLayoutItemProvider {
     val spanProvider: LazyStaggeredGridSpanProvider
+    val keyToIndexMap: LazyLayoutKeyIndexMap
 }
 
 @Composable
@@ -56,7 +57,7 @@
         LazyStaggeredGridIntervalContent(latestContent())
     }
 
-    private val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
+    override val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
         firstVisibleItemIndex = { state.firstVisibleItemIndex },
         slidingWindowSize = { 90 },
         extraItemCount = { 200 },
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt
new file mode 100644
index 0000000..5982746
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+
+/**
+ * Receiver scope for itemContent in [LazyStaggeredGridScope.item]
+ */
+@Stable
+@LazyStaggeredGridScopeMarker
+sealed interface LazyStaggeredGridItemScope {
+    /**
+     * This modifier animates the item placement within the grid.
+     *
+     * When you scroll backward staggered grids could move already visible items in order
+     * to correct the accumulated errors in previous item size estimations. This modifier
+     * can animate such moves.
+     *
+     * Aside from that when you provide a key via [LazyStaggeredGridScope.item] /
+     * [LazyStaggeredGridScope.items] this modifier will enable item reordering animations.
+     *
+     * @param animationSpec a finite animation that will be used to animate the item placement.
+     */
+    @ExperimentalFoundationApi
+    fun Modifier.animateItemPlacement(
+        animationSpec: FiniteAnimationSpec<IntOffset> = spring(
+            stiffness = Spring.StiffnessMediumLow,
+            visibilityThreshold = IntOffset.VisibilityThreshold
+        )
+    ): Modifier
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal object LazyStaggeredGridItemScopeImpl : LazyStaggeredGridItemScope {
+    @ExperimentalFoundationApi
+    override fun Modifier.animateItemPlacement(animationSpec: FiniteAnimationSpec<IntOffset>) =
+        this then AnimateItemPlacementElement(animationSpec)
+}
+
+private class AnimateItemPlacementElement(
+    val animationSpec: FiniteAnimationSpec<IntOffset>
+) : ModifierNodeElement<AnimateItemPlacementNode>() {
+
+    override fun create(): AnimateItemPlacementNode = AnimateItemPlacementNode(animationSpec)
+
+    override fun update(node: AnimateItemPlacementNode) {
+        node.delegatingNode.placementAnimationSpec = animationSpec
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is AnimateItemPlacementElement) return false
+        return animationSpec != other.animationSpec
+    }
+
+    override fun hashCode(): Int {
+        return animationSpec.hashCode()
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "animateItemPlacement"
+        value = animationSpec
+    }
+}
+
+private class AnimateItemPlacementNode(
+    animationSpec: FiniteAnimationSpec<IntOffset>
+) : DelegatingNode(), ParentDataModifierNode {
+
+    val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
+
+    override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
+}
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 fa9be08..b653fd7 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
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.fastMaxOfOrNull
-import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.findIndexByKey
 import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.FullSpan
 import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.Unset
 import androidx.compose.runtime.snapshots.Snapshot
@@ -31,6 +31,7 @@
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
 import androidx.compose.ui.util.packInts
 import androidx.compose.ui.util.unpackInt1
 import androidx.compose.ui.util.unpackInt2
@@ -76,6 +77,7 @@
 @ExperimentalFoundationApi
 internal fun LazyLayoutMeasureScope.measureStaggeredGrid(
     state: LazyStaggeredGridState,
+    pinnedItems: List<Int>,
     itemProvider: LazyStaggeredGridItemProvider,
     resolvedSlots: LazyStaggeredGridSlots,
     constraints: Constraints,
@@ -89,6 +91,7 @@
 ): LazyStaggeredGridMeasureResult {
     val context = LazyStaggeredGridMeasureContext(
         state = state,
+        pinnedItems = pinnedItems,
         itemProvider = itemProvider,
         resolvedSlots = resolvedSlots,
         constraints = constraints,
@@ -163,8 +166,9 @@
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridMeasureContext(
+internal class LazyStaggeredGridMeasureContext(
     val state: LazyStaggeredGridState,
+    val pinnedItems: List<Int>,
     val itemProvider: LazyStaggeredGridItemProvider,
     val resolvedSlots: LazyStaggeredGridSlots,
     val constraints: Constraints,
@@ -191,6 +195,8 @@
             spacing = mainAxisSpacing,
             lane = lane,
             span = span,
+            beforeContentPadding = beforeContentPadding,
+            afterContentPadding = afterContentPadding
         )
     }
 
@@ -662,7 +668,7 @@
         // even if we compose items to fill before content padding we should ignore items fully
         // located there for the state's scroll position calculation (first item + first offset)
         debugLog { "adjusting for content padding" }
-        if (beforeContentPadding > 0) {
+        if (beforeContentPadding > mainAxisSpacing) {
             for (laneIndex in measuredItems.indices) {
                 val laneItems = measuredItems[laneIndex]
                 for (i in laneItems.indices) {
@@ -674,7 +680,6 @@
                         firstItemOffsets[laneIndex] != 0 &&
                         firstItemOffsets[laneIndex] >= size
                     ) {
-
                         firstItemOffsets[laneIndex] -= size
                         firstItemIndices[laneIndex] = laneItems[i + 1].index
                     } else {
@@ -706,13 +711,22 @@
         }
 
         val mainAxisLayoutSize =
-            min(if (isVertical) layoutHeight else layoutWidth, mainAxisAvailableSize)
+            min(if (isVertical) layoutHeight else layoutWidth, mainAxisAvailableSize).let {
+                // The offsets are calculated in [-beforePad; size + afterPad] interval
+                // Ensure the layout size used for positioning (and reverse layout calculation)
+                // is in the same interval.
+                it - beforeContentPadding + afterContentPadding
+            }
 
         var extraItemOffset = itemScrollOffsets[0]
         val extraItemsBefore = calculateExtraItems(
             position = {
                 extraItemOffset -= it.sizeWithSpacings
-                it.position(0, extraItemOffset, 0, mainAxisLayoutSize)
+                it.position(
+                    mainAxis = extraItemOffset,
+                    crossAxis = 0,
+                    mainAxisLayoutSize = mainAxisLayoutSize
+                )
             },
             filter = { itemIndex ->
                 val lane = laneInfo.getLane(itemIndex)
@@ -727,7 +741,7 @@
             }
         )
 
-        val positionedItems = calculatePositionedItems(
+        val visibleItems = calculateVisibleItems(
             measuredItems,
             itemScrollOffsets,
             mainAxisLayoutSize,
@@ -736,7 +750,11 @@
         extraItemOffset = itemScrollOffsets[0]
         val extraItemsAfter = calculateExtraItems(
             position = {
-                val positionedItem = it.position(0, extraItemOffset, 0, mainAxisLayoutSize)
+                val positionedItem = it.position(
+                    mainAxis = extraItemOffset,
+                    crossAxis = 0,
+                    mainAxisLayoutSize = mainAxisLayoutSize
+                )
                 extraItemOffset += it.sizeWithSpacings
                 positionedItem
             },
@@ -756,10 +774,25 @@
             }
         )
 
+        val positionedItems = mutableListOf<LazyStaggeredGridPositionedItem>()
+        positionedItems.addAll(extraItemsBefore)
+        positionedItems.addAll(visibleItems)
+        positionedItems.addAll(extraItemsAfter)
+
         debugLog {
             "positioned: $positionedItems"
         }
 
+        state.placementAnimator.onMeasured(
+            consumedScroll = consumedScroll.toInt(),
+            layoutWidth = layoutWidth,
+            layoutHeight = layoutHeight,
+            positionedItems = positionedItems,
+            itemProvider = measuredItemProvider,
+            isVertical = isVertical,
+            laneCount = laneCount
+        )
+
         // end placement
 
         // only scroll backward if the first item is not on screen or fully visible
@@ -773,22 +806,14 @@
             firstVisibleItemScrollOffsets = firstItemOffsets,
             consumedScroll = consumedScroll,
             measureResult = layout(layoutWidth, layoutHeight) {
-                extraItemsBefore.fastForEach { item ->
-                    item.place(scope = this, context = this@measure)
-                }
-
                 positionedItems.fastForEach { item ->
                     item.place(scope = this, context = this@measure)
                 }
-
-                extraItemsAfter.fastForEach { item ->
-                    item.place(scope = this, context = this@measure)
-                }
             },
             canScrollForward = canScrollForward,
             canScrollBackward = canScrollBackward,
             isVertical = isVertical,
-            visibleItemsInfo = positionedItems,
+            visibleItemsInfo = visibleItems,
             totalItemsCount = itemCount,
             viewportSize = IntSize(layoutWidth, layoutHeight),
             viewportStartOffset = minOffset,
@@ -800,7 +825,7 @@
     }
 }
 
-private fun LazyStaggeredGridMeasureContext.calculatePositionedItems(
+private fun LazyStaggeredGridMeasureContext.calculateVisibleItems(
     measuredItems: Array<ArrayDeque<LazyStaggeredGridMeasuredItem>>,
     itemScrollOffsets: IntArray,
     mainAxisLayoutSize: Int,
@@ -823,13 +848,18 @@
         val mainAxisOffset = itemScrollOffsets.maxInRange(spanRange)
         val crossAxisOffset = resolvedSlots.positions[laneIndex]
 
-        if (item.placeables.isEmpty()) {
+        if (item.placeablesCount == 0) {
             // nothing to place, ignore spacings
             continue
         }
 
         positionedItems +=
-            item.position(laneIndex, mainAxisOffset, crossAxisOffset, mainAxisLayoutSize)
+            item.position(
+                mainAxis = mainAxisOffset,
+                crossAxis = crossAxisOffset,
+                mainAxisLayoutSize = mainAxisLayoutSize,
+                lane = laneIndex
+            )
         spanRange.forEach { lane ->
             itemScrollOffsets[lane] = mainAxisOffset + item.sizeWithSpacings
         }
@@ -844,10 +874,7 @@
 ): List<LazyStaggeredGridPositionedItem> {
     var result: MutableList<LazyStaggeredGridPositionedItem>? = null
 
-    val pinnedItems = state.pinnedItems
-    pinnedItems.fastForEach { item ->
-        val index = itemProvider.findIndexByKey(item.key, item.index)
-
+    pinnedItems.fastForEach { index ->
         if (filter(index)) {
             val spanRange = itemProvider.getSpanRange(index, 0)
             if (result == null) {
@@ -862,7 +889,7 @@
 }
 
 @JvmInline
-private value class SpanRange private constructor(val packedValue: Long) {
+internal value class SpanRange private constructor(val packedValue: Long) {
     constructor(lane: Int, span: Int) : this(packInts(lane, lane + span))
 
     inline val start get(): Int = unpackInt1(packedValue)
@@ -959,9 +986,9 @@
     laneInfo.findPreviousItemIndex(item, lane)
 
 @OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridMeasureProvider(
+internal class LazyStaggeredGridMeasureProvider(
     private val isVertical: Boolean,
-    private val itemProvider: LazyLayoutItemProvider,
+    private val itemProvider: LazyStaggeredGridItemProvider,
     private val measureScope: LazyLayoutMeasureScope,
     private val resolvedSlots: LazyStaggeredGridSlots,
     private val measuredItemFactory: MeasuredItemFactory,
@@ -989,10 +1016,12 @@
         val placeables = measureScope.measure(index, childConstraints(span.start, span.size))
         return measuredItemFactory.createItem(index, span.start, span.size, key, placeables)
     }
+
+    val keyToIndexMap: LazyLayoutKeyIndexMap get() = itemProvider.keyToIndexMap
 }
 
 // This interface allows to avoid autoboxing on index param
-private fun interface MeasuredItemFactory {
+internal fun interface MeasuredItemFactory {
     fun createItem(
         index: Int,
         lane: Int,
@@ -1002,17 +1031,23 @@
     ): LazyStaggeredGridMeasuredItem
 }
 
-private class LazyStaggeredGridMeasuredItem(
+internal class LazyStaggeredGridMeasuredItem(
     val index: Int,
     val key: Any,
-    val placeables: List<Placeable>,
-    val isVertical: Boolean,
-    val spacing: Int,
+    private val placeables: List<Placeable>,
+    private val isVertical: Boolean,
+    spacing: Int,
     val lane: Int,
     val span: Int,
+    private val beforeContentPadding: Int,
+    private val afterContentPadding: Int,
 ) {
     var isVisible = true
 
+    val placeablesCount: Int get() = placeables.size
+
+    fun getParentData(index: Int) = placeables[index].parentData
+
     val mainAxisSize: Int = placeables.fastMaxOfOrNull { placeable ->
         if (isVertical) placeable.height else placeable.width
     } ?: 0
@@ -1024,10 +1059,10 @@
     } ?: 0
 
     fun position(
-        lane: Int,
         mainAxis: Int,
         crossAxis: Int,
         mainAxisLayoutSize: Int,
+        lane: Int = 0
     ): LazyStaggeredGridPositionedItem =
         LazyStaggeredGridPositionedItem(
             offset = if (isVertical) {
@@ -1039,46 +1074,74 @@
             index = index,
             key = key,
             size = if (isVertical) {
-                IntSize(crossAxisSize, sizeWithSpacings)
+                IntSize(crossAxisSize, mainAxisSize)
             } else {
-                IntSize(sizeWithSpacings, crossAxisSize)
+                IntSize(mainAxisSize, crossAxisSize)
             },
             placeables = placeables,
             isVertical = isVertical,
             mainAxisLayoutSize = mainAxisLayoutSize,
+            minMainAxisOffset = -beforeContentPadding,
+            maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding,
+            span = span
         )
 }
 
-@OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridPositionedItem(
+internal class LazyStaggeredGridPositionedItem(
     override val offset: IntOffset,
     override val index: Int,
     override val lane: Int,
     override val key: Any,
     override val size: IntSize,
     private val placeables: List<Placeable>,
-    private val isVertical: Boolean,
+    val isVertical: Boolean,
     private val mainAxisLayoutSize: Int,
+    private val minMainAxisOffset: Int,
+    private val maxMainAxisOffset: Int,
+    val span: Int
 ) : LazyStaggeredGridItemInfo {
+
+    val placeablesCount: Int get() = placeables.size
+
+    val mainAxisSize get() = if (isVertical) size.height else size.width
+
+    val crossAxisOffset get() = if (isVertical) offset.x else offset.y
+
+    fun getParentData(index: Int) = placeables[index].parentData
+
     fun place(
         scope: Placeable.PlacementScope,
         context: LazyStaggeredGridMeasureContext
     ) = with(context) {
         with(scope) {
-            placeables.fastForEach { placeable ->
-                val reverseLayoutAwareOffset = if (reverseLayout) {
-                    offset.copy { mainAxisOffset ->
+            placeables.fastForEachIndexed { index, placeable ->
+                val minOffset = minMainAxisOffset - placeable.mainAxisSize
+                val maxOffset = maxMainAxisOffset
+
+                var offset = offset
+                val animateNode = getParentData(index) as? LazyLayoutAnimateItemModifierNode
+                if (animateNode != null) {
+                    val animatedOffset = offset + animateNode.placementDelta
+                    // cancel the animation if current and target offsets are both out of the bounds.
+                    if ((offset.mainAxis <= minOffset && animatedOffset.mainAxis <= minOffset) ||
+                        (offset.mainAxis >= maxOffset && animatedOffset.mainAxis >= maxOffset)
+                    ) {
+                        animateNode.cancelAnimation()
+                    }
+                    offset = animatedOffset
+                }
+                if (reverseLayout) {
+                    offset = offset.copy { mainAxisOffset ->
                         mainAxisLayoutSize - mainAxisOffset - placeable.mainAxisSize
                     }
-                } else {
-                    offset
                 }
-
-                placeable.placeRelativeWithLayer(reverseLayoutAwareOffset + contentOffset)
+                offset += contentOffset
+                placeable.placeRelativeWithLayer(offset)
             }
         }
     }
 
+    private val IntOffset.mainAxis get() = if (isVertical) y else x
     private inline val Placeable.mainAxisSize get() = if (isVertical) height else width
     private inline fun IntOffset.copy(mainAxisMap: (Int) -> Int): IntOffset =
         IntOffset(if (isVertical) x else mainAxisMap(x), if (isVertical) mainAxisMap(y) else y)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
index 1f36802..c72c114 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.calculateEndPadding
 import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.unit.Constraints
@@ -96,8 +97,14 @@
             calculateTopPadding() + calculateBottomPadding()
         }.roundToPx()
 
+        val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+            state.pinnedItems,
+            state.beyondBoundsInfo
+        )
+
         measureStaggeredGrid(
             state = state,
+            pinnedItems = pinnedItems,
             itemProvider = itemProvider,
             resolvedSlots = resolvedSlots,
             constraints = constraints.copy(
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 661a73c..a1b62d6 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
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.lazy.layout.LazyAnimateScrollScope
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle
@@ -156,7 +157,8 @@
     /** implementation of [LazyAnimateScrollScope] scope required for [animateScrollToItem] **/
     private val animateScrollScope = LazyStaggeredGridAnimateScrollScope(this)
 
-    private var remeasurement: Remeasurement? = null
+    internal var remeasurement: Remeasurement? = null
+        private set
 
     internal val remeasurementModifier = object : RemeasurementModifier {
         override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
@@ -164,6 +166,8 @@
         }
     }
 
+    internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
     /**
      * Only used for testing to disable prefetching when needed to test the main logic.
      */
@@ -210,6 +214,8 @@
      */
     internal val pinnedItems = LazyLayoutPinnedItemList()
 
+    internal val placementAnimator = LazyStaggeredGridItemPlacementAnimator()
+
     /**
      * Call this function to take control of scrolling and gain the ability to send scroll events
      * via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
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 5bdc33a..5d8d4093 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
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
 import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
 import androidx.compose.foundation.lazy.NearestItemsExtraItemCount
 import androidx.compose.foundation.lazy.NearestItemsSlidingWindowSize
 import androidx.compose.foundation.lazy.layout.IntervalList
@@ -105,8 +104,6 @@
         key = key
     ) { state.pageCount }
 
-    val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
-
     val measurePolicy = rememberPagerMeasurePolicy(
         state = state,
         contentPadding = contentPadding,
@@ -119,7 +116,6 @@
         verticalAlignment = verticalAlignment,
         itemProvider = pagerItemProvider,
         pageCount = { state.pageCount },
-        beyondBoundsInfo = beyondBoundsInfo
     )
 
     val pagerFlingBehavior = remember(flingBehavior, state) {
@@ -152,7 +148,12 @@
                 reverseScrolling = reverseLayout
             )
             .clipScrollableContainer(orientation)
-            .pagerBeyondBoundsModifier(state, beyondBoundsInfo, reverseLayout, orientation)
+            .pagerBeyondBoundsModifier(
+                state,
+                beyondBoundsPageCount,
+                reverseLayout,
+                orientation
+            )
             .overscroll(overscrollEffect)
             .scrollable(
                 orientation = orientation,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
index bc26545..7c9deaf 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
@@ -17,8 +17,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
-import androidx.compose.foundation.lazy.layout.BeyondBoundsState
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -34,21 +33,21 @@
 @Composable
 internal fun Modifier.pagerBeyondBoundsModifier(
     state: PagerState,
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    beyondBoundsPageCount: Int,
     reverseLayout: Boolean,
     orientation: Orientation
 ): Modifier {
     val layoutDirection = LocalLayoutDirection.current
     return this then remember(
         state,
-        beyondBoundsInfo,
+        beyondBoundsPageCount,
         reverseLayout,
         layoutDirection,
         orientation
     ) {
         LazyLayoutBeyondBoundsModifierLocal(
-            PagerBeyondBoundsState(state),
-            beyondBoundsInfo,
+            PagerBeyondBoundsState(state, beyondBoundsPageCount),
+            state.beyondBoundsInfo,
             reverseLayout,
             layoutDirection,
             orientation
@@ -57,7 +56,10 @@
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-internal class PagerBeyondBoundsState(val state: PagerState) : BeyondBoundsState {
+internal class PagerBeyondBoundsState(
+    private val state: PagerState,
+    private val beyondBoundsPageCount: Int
+) : LazyLayoutBeyondBoundsState {
     override fun remeasure() {
         state.remeasurement?.forceRemeasure()
     }
@@ -66,8 +68,11 @@
         get() = state.layoutInfo.pagesCount
     override val hasVisibleItems: Boolean
         get() = state.layoutInfo.visiblePagesInfo.isNotEmpty()
-    override val firstVisibleIndex: Int
-        get() = state.firstVisiblePage
-    override val lastVisibleIndex: Int
-        get() = state.layoutInfo.visiblePagesInfo.last().index
+    override val firstPlacedIndex: Int
+        get() = maxOf(0, state.firstVisiblePage - beyondBoundsPageCount)
+    override val lastPlacedIndex: Int
+        get() = minOf(
+            itemCount - 1,
+            state.layoutInfo.visiblePagesInfo.last().index + beyondBoundsPageCount
+        )
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 6785dea..68693ac 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -20,9 +20,7 @@
 import androidx.compose.foundation.fastFilter
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.Placeable
@@ -35,7 +33,6 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMaxBy
 import kotlin.math.abs
-import kotlin.math.min
 import kotlin.math.roundToInt
 import kotlin.math.sign
 
@@ -58,8 +55,7 @@
     visualPageOffset: IntOffset,
     pageAvailableSize: Int,
     beyondBoundsPageCount: Int,
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
-    pinnedPages: LazyLayoutPinnedItemList,
+    pinnedPages: List<Int>,
     layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
 ): PagerMeasureResult {
     require(beforeContentPadding >= 0)
@@ -289,9 +285,7 @@
 
         // Compose extra pages before
         val extraPagesBefore = createPagesBeforeList(
-            beyondBoundsInfo = beyondBoundsInfo,
             currentFirstPage = currentFirstPage,
-            pagesCount = pageCount,
             beyondBoundsPageCount = beyondBoundsPageCount,
             pinnedPages = pinnedPages
         ) {
@@ -318,8 +312,7 @@
 
         // Compose pages after last page
         val extraPagesAfter = createPagesAfterList(
-            beyondBoundsInfo = beyondBoundsInfo,
-            visiblePages = visiblePages,
+            currentLastPage = visiblePages.last().index,
             pagesCount = pageCount,
             beyondBoundsPageCount = beyondBoundsPageCount,
             pinnedPages = pinnedPages
@@ -440,59 +433,44 @@
     return itemCurrentPosition - desiredDistance
 }
 
-@OptIn(ExperimentalFoundationApi::class)
 private fun createPagesAfterList(
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
-    visiblePages: MutableList<MeasuredPage>,
+    currentLastPage: Int,
     pagesCount: Int,
     beyondBoundsPageCount: Int,
-    pinnedPages: LazyLayoutPinnedItemList,
+    pinnedPages: List<Int>,
     getAndMeasure: (Int) -> MeasuredPage
 ): List<MeasuredPage> {
-    fun LazyListBeyondBoundsInfo.endIndex() = min(end, pagesCount - 1)
-
     var list: MutableList<MeasuredPage>? = null
 
-    var end = visiblePages.last().index
+    val end = minOf(currentLastPage + beyondBoundsPageCount, pagesCount - 1)
 
     fun addPage(index: Int) {
         if (list == null) list = mutableListOf()
         requireNotNull(list).add(getAndMeasure(index))
     }
 
-    if (beyondBoundsInfo.hasIntervals()) {
-        end = maxOf(beyondBoundsInfo.endIndex(), end)
-    }
-
-    end = minOf(end + beyondBoundsPageCount, pagesCount - 1)
-
-    for (i in visiblePages.last().index + 1..end) {
+    for (i in currentLastPage + 1..end) {
         addPage(i)
     }
 
-    pinnedPages.fastForEach { page ->
-        if (page.index in (end + 1) until pagesCount) {
-            addPage(page.index)
+    pinnedPages.fastForEach { pageIndex ->
+        if (pageIndex in (end + 1) until pagesCount) {
+            addPage(pageIndex)
         }
     }
 
     return list ?: emptyList()
 }
 
-@OptIn(ExperimentalFoundationApi::class)
 private fun createPagesBeforeList(
-    beyondBoundsInfo: LazyListBeyondBoundsInfo,
     currentFirstPage: Int,
-    pagesCount: Int,
     beyondBoundsPageCount: Int,
-    pinnedPages: LazyLayoutPinnedItemList,
+    pinnedPages: List<Int>,
     getAndMeasure: (Int) -> MeasuredPage
 ): List<MeasuredPage> {
-    fun LazyListBeyondBoundsInfo.startIndex() = min(start, pagesCount - 1)
-
     var list: MutableList<MeasuredPage>? = null
 
-    var start = currentFirstPage
+    val start = maxOf(0, currentFirstPage - beyondBoundsPageCount)
 
     fun addPage(index: Int) {
         if (list == null) list = mutableListOf()
@@ -501,19 +479,13 @@
         )
     }
 
-    if (beyondBoundsInfo.hasIntervals()) {
-        start = minOf(beyondBoundsInfo.startIndex(), start)
-    }
-
-    start = maxOf(0, start - beyondBoundsPageCount)
-
     for (i in currentFirstPage - 1 downTo start) {
         addPage(i)
     }
 
-    pinnedPages.fastForEach { page ->
-        if (page.index < start) {
-            addPage(page.index)
+    pinnedPages.fastForEach { pageIndex ->
+        if (pageIndex < start) {
+            addPage(pageIndex)
         }
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
index 0c56d50..2049609 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
@@ -22,8 +22,8 @@
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.calculateEndPadding
 import androidx.compose.foundation.layout.calculateStartPadding
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.snapshots.Snapshot
@@ -51,7 +51,6 @@
     horizontalAlignment: Alignment.Horizontal?,
     verticalAlignment: Alignment.Vertical?,
     pageCount: () -> Int,
-    beyondBoundsInfo: LazyListBeyondBoundsInfo
 ) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
     contentPadding,
     pageSpacing,
@@ -63,7 +62,6 @@
     horizontalAlignment,
     verticalAlignment,
     pageCount,
-    beyondBoundsInfo
 ) {
     { containerConstraints ->
         val isVertical = orientation == Orientation.Vertical
@@ -152,6 +150,11 @@
             }
         }
 
+        val pinnedPages = itemProvider.calculateLazyLayoutPinnedIndices(
+            pinnedItemList = state.pinnedPages,
+            beyondBoundsInfo = state.beyondBoundsInfo
+        )
+
         measurePager(
             beforeContentPadding = beforeContentPadding,
             afterContentPadding = afterContentPadding,
@@ -170,8 +173,7 @@
             pagerItemProvider = itemProvider,
             reverseLayout = reverseLayout,
             scrollToBeConsumed = state.scrollToBeConsumed,
-            beyondBoundsInfo = beyondBoundsInfo,
-            pinnedPages = state.pinnedPages,
+            pinnedPages = pinnedPages,
             layout = { width, height, placement ->
                 layout(
                     containerConstraints.constrainWidth(width + totalHorizontalPadding),
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 690518b..23e45b8 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
@@ -28,6 +28,7 @@
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.runtime.Composable
@@ -361,6 +362,8 @@
 
     internal val prefetchState = LazyLayoutPrefetchState()
 
+    internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
     /**
      * Provides a modifier which allows to delay some interactions (e.g. scroll)
      * until layout is ready.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
index b0c7eaf..2288769 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
@@ -120,10 +120,9 @@
         return BringIntoViewRequesterNode(requester)
     }
 
-    override fun update(node: BringIntoViewRequesterNode): BringIntoViewRequesterNode =
-        node.also {
-            it.updateRequester(requester)
-        }
+    override fun update(node: BringIntoViewRequesterNode) {
+        node.updateRequester(requester)
+    }
 
     override fun InspectorInfo.inspectableProperties() {
         name = "bringIntoViewRequester"
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
index 9e31be2..1c91070 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
@@ -106,8 +106,8 @@
 ) : ModifierNodeElement<BringIntoViewResponderNode>() {
     override fun create(): BringIntoViewResponderNode = BringIntoViewResponderNode(responder)
 
-    override fun update(node: BringIntoViewResponderNode) = node.also {
-        it.responder = responder
+    override fun update(node: BringIntoViewResponderNode) {
+        node.responder = responder
     }
     override fun equals(other: Any?): Boolean {
         return (this === other) ||
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
index b2293d4..4d22aeb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
@@ -60,7 +60,7 @@
 
     override fun update(
         node: SelectableTextAnnotatedStringNode
-    ): SelectableTextAnnotatedStringNode {
+    ) {
         node.update(
             text = text,
             style = style,
@@ -74,7 +74,6 @@
             onPlaceholderLayout = onPlaceholderLayout,
             selectionController = selectionController
         )
-        return node
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
index 9680f3b..cac5e39 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
@@ -30,7 +30,7 @@
 import androidx.compose.ui.node.GlobalPositionAwareModifierNode
 import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
 import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.Placeholder
@@ -60,7 +60,7 @@
 ) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode,
     SemanticsModifierNode {
 
-    private val delegate = delegated {
+    private val delegate = delegate(
         TextAnnotatedStringNode(
             text = text,
             style = style,
@@ -74,7 +74,7 @@
             onPlaceholderLayout = onPlaceholderLayout,
             selectionController = selectionController
         )
-    }
+    )
 
     init {
         requireNotNull(selectionController) {
@@ -149,6 +149,6 @@
             )
         )
         // we always relayout when we're selectable
-        invalidateMeasurements()
+        invalidateMeasurement()
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
index 377e096..6232e52 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
@@ -60,7 +60,7 @@
         selectionController
     )
 
-    override fun update(node: TextAnnotatedStringNode): TextAnnotatedStringNode {
+    override fun update(node: TextAnnotatedStringNode) {
         node.doInvalidations(
             textChanged = node.updateText(
                 text = text
@@ -80,7 +80,6 @@
                 selectionController = selectionController
             )
         )
-        return node
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
index f948a2f..7d4e8d4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
@@ -34,7 +34,7 @@
 import androidx.compose.ui.node.SemanticsModifierNode
 import androidx.compose.ui.node.invalidateDraw
 import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
 import androidx.compose.ui.node.invalidateSemantics
 import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.getTextLayoutResult
@@ -200,7 +200,7 @@
                 minLines = minLines,
                 placeholders = placeholders
             )
-            invalidateMeasurements()
+            invalidateMeasurement()
             invalidateDraw()
         }
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
index 63be1b7..bb846b3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
@@ -49,7 +49,7 @@
         minLines
     )
 
-    override fun update(node: TextStringSimpleNode): TextStringSimpleNode {
+    override fun update(node: TextStringSimpleNode) {
         node.doInvalidations(
             textChanged = node.updateText(
                 text = text
@@ -63,7 +63,6 @@
                 overflow = overflow
             )
         )
-        return node
     }
 
     override fun equals(other: Any?): Boolean {
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 93c1391..ca2ea56 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
@@ -37,7 +37,7 @@
 import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.SemanticsModifierNode
 import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
 import androidx.compose.ui.node.invalidateSemantics
 import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.getTextLayoutResult
@@ -168,7 +168,7 @@
                 maxLines = maxLines,
                 minLines = minLines
             )
-            invalidateMeasurements()
+            invalidateMeasurement()
             invalidateLayer()
         }
     }
diff --git a/compose/material/material-icons-extended/build.gradle b/compose/material/material-icons-extended/build.gradle
index 4a58d27..0903147 100644
--- a/compose/material/material-icons-extended/build.gradle
+++ b/compose/material/material-icons-extended/build.gradle
@@ -86,8 +86,6 @@
     }
 }
 
-def allThemes = ["filled", "outlined", "rounded", "sharp", "twotone"]
-
 configurations {
     embedThemesDebug {
         attributes {
@@ -105,10 +103,17 @@
 }
 
 if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    for (themeName in allThemes) {
-        def otherProject = project(":compose:material:material-icons-extended-" + themeName)
-        project.dependencies.add("embedThemesDebug", otherProject)
-        project.dependencies.add("embedThemesRelease", otherProject)
+    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.
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index 0a71de1..d9fe905 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -777,7 +777,7 @@
     method public final androidx.compose.runtime.State<java.lang.Float> getOffset();
     method public final androidx.compose.runtime.State<java.lang.Float> getOverflow();
     method public final androidx.compose.material.SwipeProgress<T> getProgress();
-    method public final T! getTargetValue();
+    method public final T getTargetValue();
     method public final boolean isAnimationRunning();
     method public final float performDrag(float delta);
     method public final suspend Object? performFling(float velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -788,7 +788,7 @@
     property public final androidx.compose.runtime.State<java.lang.Float> offset;
     property public final androidx.compose.runtime.State<java.lang.Float> overflow;
     property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.SwipeProgress<T> progress;
-    property @androidx.compose.material.ExperimentalMaterialApi public final T! targetValue;
+    property @androidx.compose.material.ExperimentalMaterialApi public final T targetValue;
     field public static final androidx.compose.material.SwipeableState.Companion Companion;
   }
 
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index cc48985..82e2838 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -39,7 +39,7 @@
         api(project(":compose:material:material-ripple"))
         api("androidx.compose.runtime:runtime:1.2.1")
         api("androidx.compose.ui:ui:1.2.1")
-        api("androidx.compose.ui:ui-text:1.2.1")
+        api(project(":compose:ui:ui-text"))
 
         implementation(libs.kotlinStdlibCommon)
         implementation("androidx.compose.animation:animation:1.2.1")
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 257e39c..dbd9be9 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
@@ -52,16 +52,15 @@
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.take
-import kotlinx.coroutines.launch
 import kotlin.math.PI
 import kotlin.math.abs
 import kotlin.math.sign
 import kotlin.math.sin
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
 
 /**
  * State of the [swipeable] modifier.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
index 92c174f..78112ad2 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
@@ -25,8 +25,8 @@
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.rememberScrollState
@@ -133,10 +133,10 @@
     contentColor: Color = contentColorFor(backgroundColor),
     indicator: @Composable @UiComposable
         (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
-            TabRowDefaults.Indicator(
-                Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
-            )
-        },
+        TabRowDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
+        )
+    },
     divider: @Composable @UiComposable () -> Unit =
         @Composable {
             TabRowDefaults.Divider()
@@ -228,10 +228,10 @@
     edgePadding: Dp = TabRowDefaults.ScrollableTabRowPadding,
     indicator: @Composable @UiComposable
         (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
-            TabRowDefaults.Indicator(
-                Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
-            )
-        },
+        TabRowDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
+        )
+    },
     divider: @Composable @UiComposable () -> Unit =
         @Composable {
             TabRowDefaults.Divider()
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
index d647498..618aff5d 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
@@ -115,10 +115,11 @@
             LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
+
+    BasicText(
+        text = text,
+        modifier = modifier,
+        style = style.merge(
             color = textColor,
             fontSize = fontSize,
             fontWeight = fontWeight,
@@ -128,12 +129,7 @@
             textDecoration = textDecoration,
             fontStyle = fontStyle,
             letterSpacing = letterSpacing
-        )
-    )
-    BasicText(
-        text = text,
-        modifier = modifier,
-        style = mergedStyle,
+        ),
         onTextLayout = onTextLayout,
         overflow = overflow,
         softWrap = softWrap,
@@ -266,10 +262,11 @@
             LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
+
+    BasicText(
+        text = text,
+        modifier = modifier,
+        style = style.merge(
             color = textColor,
             fontSize = fontSize,
             fontWeight = fontWeight,
@@ -279,12 +276,7 @@
             textDecoration = textDecoration,
             fontStyle = fontStyle,
             letterSpacing = letterSpacing
-        )
-    )
-    BasicText(
-        text = text,
-        modifier = modifier,
-        style = mergedStyle,
+        ),
         onTextLayout = onTextLayout,
         overflow = overflow,
         softWrap = softWrap,
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 0901450..d2b53fb 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -818,7 +818,7 @@
 
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index d920d2a..c580cf9 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -1204,7 +1204,7 @@
 
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 0901450..d2b53fb 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -818,7 +818,7 @@
 
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
     method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index 10979b6..f2acb079 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -38,15 +38,15 @@
         implementation(libs.kotlinStdlibCommon)
         implementation("androidx.activity:activity-compose:1.5.0")
         implementation("androidx.compose.animation:animation-core:1.4.2")
-        implementation("androidx.compose.foundation:foundation-layout:1.4.2")
+        implementation(project(":compose:foundation:foundation-layout"))
         implementation("androidx.compose.ui:ui-util:1.4.2")
-        api("androidx.compose.foundation:foundation:1.4.2")
+        api(project(":compose:foundation:foundation"))
         api("androidx.compose.material:material-icons-core:1.4.2")
         api("androidx.compose.material:material-ripple:1.4.2")
         api("androidx.compose.runtime:runtime:1.4.2")
         api("androidx.compose.ui:ui-graphics:1.4.2")
         api("androidx.compose.ui:ui:1.4.2")
-        api("androidx.compose.ui:ui-text:1.4.2")
+        api(project(":compose:ui:ui-text"))
 
         // TODO: remove next 3 dependencies when b/202810604 is fixed
         implementation("androidx.savedstate:savedstate-ktx:1.2.1")
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
index 9f0f5e4..2e60a0a 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material3
 
 import android.os.Build
+import android.os.Build.VERSION.SDK_INT
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
@@ -32,6 +33,8 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Assume
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -47,6 +50,13 @@
     @get:Rule
     val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
 
+    @Before
+    fun before() {
+        // Disable tests for API 33 until a solution can be found
+        // to make these tests stable for Firebase API 33 tests.
+        Assume.assumeTrue(SDK_INT != 33)
+    }
+
     @Test
     fun plainTooltip_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) { PlainTooltipTest() }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
index a770a50..ae11486 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
@@ -207,8 +207,9 @@
     paddingValues: PaddingValues,
     content: @Composable RowScope.() -> Unit,
 ) {
+    val semanticModifier = Modifier.semantics(mergeDescendants = true) { }.then(modifier)
     Surface(
-        modifier = modifier,
+        modifier = semanticModifier,
         shape = shape,
         color = containerColor,
         contentColor = contentColor,
@@ -218,8 +219,7 @@
         Row(
             modifier = Modifier
                 .heightIn(min = minHeight)
-                .padding(paddingValues)
-                .semantics(mergeDescendants = true) {},
+                .padding(paddingValues),
             content = content
         )
     }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
index c418cf7..c16d817 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
@@ -105,7 +105,7 @@
     softWrap: Boolean = true,
     maxLines: Int = Int.MAX_VALUE,
     minLines: Int = 1,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     style: TextStyle = LocalTextStyle.current
 ) {
 
@@ -114,10 +114,11 @@
             LocalContentColor.current
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
+
+    BasicText(
+        text,
+        modifier,
+        style.merge(
             color = textColor,
             fontSize = fontSize,
             fontWeight = fontWeight,
@@ -127,12 +128,7 @@
             textDecoration = textDecoration,
             fontStyle = fontStyle,
             letterSpacing = letterSpacing
-        )
-    )
-    BasicText(
-        text,
-        modifier,
-        mergedStyle,
+        ),
         onTextLayout,
         overflow,
         softWrap,
@@ -263,10 +259,11 @@
             LocalContentColor.current
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
+
+    BasicText(
+        text = text,
+        modifier = modifier,
+        style = style.merge(
             color = textColor,
             fontSize = fontSize,
             fontWeight = fontWeight,
@@ -276,12 +273,7 @@
             textDecoration = textDecoration,
             fontStyle = fontStyle,
             letterSpacing = letterSpacing
-        )
-    )
-    BasicText(
-        text = text,
-        modifier = modifier,
-        style = mergedStyle,
+        ),
         onTextLayout = onTextLayout,
         overflow = overflow,
         softWrap = softWrap,
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 59a556b..3ddd28d 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -200,8 +200,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T current;
   }
 
   @androidx.compose.runtime.Stable public final class CompositionLocalContext {
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 9ce9384..1bea3e6 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -218,8 +218,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T current;
   }
 
   @androidx.compose.runtime.Stable public final class CompositionLocalContext {
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 366b6db..55a4ec2 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -218,8 +218,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T current;
   }
 
   @androidx.compose.runtime.Stable public final class CompositionLocalContext {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
index 243dc42..8f2683a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
@@ -28,5 +28,5 @@
      * IMPORTANT: Whenever updating this value, please make sure to also update `versionTable` and
      * `minimumRuntimeVersionInt` in `VersionChecker.kt` of the compiler.
      */
-    const val version: Int = 9901
+    const val version: Int = 10001
 }
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
index 51ee797..04c5b531 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
@@ -54,14 +54,25 @@
     fun textLikeSize() = compositionTest {
         slotExpect(
             name = "TextLike",
-            noMoreGroupsThan = 11,
-            noMoreSlotsThan = 15
+            noMoreGroupsThan = 5,
+            noMoreSlotsThan = 4
         ) {
             TextLike("")
         }
     }
 
     @Test
+    fun basicTextLikeSize() = compositionTest {
+        slotExpect(
+            name = "TextLike",
+            noMoreGroupsThan = 9,
+            noMoreSlotsThan = 13
+        ) {
+            BasicTextLike("")
+        }
+    }
+
+    @Test
     fun checkboxLike() = compositionTest {
         slotExpect(
             name = "CheckboxLike",
@@ -290,7 +301,19 @@
 ) {
     @Stable
     @Suppress("UNUSED_PARAMETER")
-    fun merge(other: TextStyle? = null) = this
+    fun merge2(
+        color: Color,
+        fontSize: TextUnit,
+        fontWeight: FontWeight?,
+        textAlign: TextAlign?,
+        lineHeight: TextUnit,
+        fontFamily: FontFamily?,
+        textDecoration: TextDecoration?,
+        fontStyle: FontStyle?,
+        letterSpacing: TextUnit
+    ): TextStyle {
+        return this
+    }
 
     companion object {
         val Default = TextStyle()
@@ -345,21 +368,18 @@
         }
     }
 
-    val mergedStyle = style.merge(
-        TextStyle(
-            color = textColor,
-            fontSize = fontSize,
-            fontWeight = fontWeight,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            fontFamily = fontFamily,
-            textDecoration = textDecoration,
-            fontStyle = fontStyle,
-            letterSpacing = letterSpacing
-        )
+    val mergedStyle = style.merge2(
+        color = textColor,
+        fontSize = fontSize,
+        fontWeight = fontWeight,
+        textAlign = textAlign,
+        lineHeight = lineHeight,
+        fontFamily = fontFamily,
+        textDecoration = textDecoration,
+        fontStyle = fontStyle,
+        letterSpacing = letterSpacing
     )
-
-    BasicTextLike(
+    EmptyBasicTextLikeComposable(
         text = text,
         modifier = modifier,
         style = mergedStyle,
@@ -371,6 +391,22 @@
     )
 }
 
+/**
+ * This composable adds no internal overhead, to isolate material text details
+ */
+@Suppress("UNUSED_PARAMETER")
+@Composable
+private fun EmptyBasicTextLikeComposable(
+    text: String,
+    modifier: Modifier = Modifier,
+    style: TextStyle = TextStyle.Default,
+    onTextLayout: (TextLayoutResult) -> Unit = {},
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = 1
+) = Unit
+
 private fun CompositionTestScope.slotExpect(
     name: String,
     noMoreGroupsThan: Int,
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index d818cd8..7b32178 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -623,6 +623,7 @@
     method public void relativeMoveTo(float dx, float dy);
     method public void relativeQuadraticBezierTo(float dx1, float dy1, float dx2, float dy2);
     method public void reset();
+    method public default void rewind();
     method public void setFillType(int);
     method public void translate(long offset);
     property public abstract int fillType;
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 b8961c4..f7a4628 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -626,6 +626,7 @@
     method public void relativeMoveTo(float dx, float dy);
     method public void relativeQuadraticBezierTo(float dx1, float dy1, float dx2, float dy2);
     method public void reset();
+    method public default void rewind();
     method public void setFillType(int);
     method public void translate(long offset);
     property public abstract int fillType;
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 63742f0..172d06a 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -658,6 +658,7 @@
     method public void relativeMoveTo(float dx, float dy);
     method public void relativeQuadraticBezierTo(float dx1, float dy1, float dx2, float dy2);
     method public void reset();
+    method public default void rewind();
     method public void setFillType(int);
     method public void translate(long offset);
     property public abstract int fillType;
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
index dafe263..6975ef7 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
@@ -25,6 +25,8 @@
 import org.junit.runner.RunWith
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import kotlin.math.PI
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -78,4 +80,30 @@
             ]
         )
     }
+
+    @Test
+    fun testRewindPath() {
+        val androidPath = TestAndroidPath()
+        val path = androidPath.asComposePath().apply {
+            addRect(Rect(0f, 0f, 100f, 200f))
+        }
+        assertFalse(path.isEmpty)
+
+        path.rewind()
+
+        assertTrue(path.isEmpty)
+        // Reset should not be invoked as the rewind method is implemented to call into the
+        // corresponding rewind call in the framework and not call the default fallback
+        assertEquals(0, androidPath.resetCount)
+    }
+
+    class TestAndroidPath : android.graphics.Path() {
+
+        var resetCount = 0
+
+        override fun reset() {
+            resetCount++
+            super.reset()
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
index 0907e2c..18705bc 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
@@ -180,6 +180,10 @@
         internalPath.reset()
     }
 
+    override fun rewind() {
+        internalPath.rewind()
+    }
+
     override fun translate(offset: Offset) {
         mMatrix.reset()
         mMatrix.setTranslate(offset.x, offset.y)
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
index 0ca99d1..b12273e 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
@@ -218,6 +218,16 @@
     fun reset()
 
     /**
+     * Rewinds the path: clears any lines and curves from the path but keeps the internal data
+     * structure for faster reuse.
+     */
+    fun rewind() {
+        // Call reset to avoid AbstractMethodAdded lint API errors. Implementations are already
+        // calling into the respective platform Path#rewind equivalent.
+        reset()
+    }
+
+    /**
      * Translates all the segments of every subpath by the given offset.
      */
     fun translate(offset: Offset)
diff --git a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
index 1c3db34..92b808d 100644
--- a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
+++ b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
@@ -179,6 +179,10 @@
             // NO-OP
         }
 
+        override fun rewind() {
+            // NO-OP
+        }
+
         override fun translate(offset: Offset) {
             // NO-OP
         }
diff --git a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
index 0cd3bbb..332b62b 100644
--- a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
+++ b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
@@ -20,6 +20,8 @@
 import androidx.compose.ui.geometry.RoundRect
 import androidx.compose.ui.test.InternalTestApi
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertFalse
 import org.junit.Test
 
 @OptIn(InternalTestApi::class)
@@ -211,4 +213,16 @@
 
         assertEquals(PathFillType.EvenOdd, path.fillType)
     }
+
+    @Test
+    fun testRewind() {
+        val path = Path().apply {
+            addRect(Rect(0f, 0f, 100f, 200f))
+        }
+        assertFalse(path.isEmpty)
+
+        path.rewind()
+
+        assertTrue(path.isEmpty)
+    }
 }
diff --git a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
index 5552b80..403e36e 100644
--- a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
+++ b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
@@ -164,6 +164,10 @@
         this.fillType = fillType
     }
 
+    override fun rewind() {
+        internalPath.rewind()
+    }
+
     override fun translate(offset: Offset) {
         internalPath.transform(Matrix33.makeTranslate(offset.x, offset.y))
     }
diff --git a/compose/ui/ui-text/api/current.ignore b/compose/ui/ui-text/api/current.ignore
index 7670599..bf842f8 100644
--- a/compose/ui/ui-text/api/current.ignore
+++ b/compose/ui/ui-text/api/current.ignore
@@ -1,4 +1,10 @@
 // Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.ui.text.Paragraph#paint(androidx.compose.ui.graphics.Canvas, androidx.compose.ui.graphics.Brush, float, androidx.compose.ui.graphics.Shadow, androidx.compose.ui.text.style.TextDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle, int):
+    Added method androidx.compose.ui.text.Paragraph.paint(androidx.compose.ui.graphics.Canvas,androidx.compose.ui.graphics.Brush,float,androidx.compose.ui.graphics.Shadow,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.graphics.drawscope.DrawStyle,int)
+AddedAbstractMethod: androidx.compose.ui.text.Paragraph#paint(androidx.compose.ui.graphics.Canvas, long, androidx.compose.ui.graphics.Shadow, androidx.compose.ui.text.style.TextDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle, int):
+    Added method androidx.compose.ui.text.Paragraph.paint(androidx.compose.ui.graphics.Canvas,long,androidx.compose.ui.graphics.Shadow,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.graphics.drawscope.DrawStyle,int)
+
+
 ChangedType: androidx.compose.ui.text.AnnotatedString.Builder#append(char):
     Method androidx.compose.ui.text.AnnotatedString.Builder.append has changed return type from androidx.compose.ui.text.AnnotatedString.Builder to void
 
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index f05e380..96355b0 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -128,7 +128,9 @@
     method public float getWidth();
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
-    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method @Deprecated public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
     property public final boolean didExceedMaxLines;
     property public final float firstBaseline;
     property public final float height;
@@ -186,6 +188,8 @@
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
     method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
     property public abstract boolean didExceedMaxLines;
     property public abstract float firstBaseline;
     property public abstract float height;
@@ -220,12 +224,14 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ParagraphStyle {
-    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+    method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
     method public androidx.compose.ui.text.style.Hyphens? getHyphens();
     method public androidx.compose.ui.text.style.LineBreak? getLineBreak();
     method public long getLineHeight();
@@ -234,6 +240,7 @@
     method public androidx.compose.ui.text.style.TextAlign? getTextAlign();
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.ParagraphStyle plus(androidx.compose.ui.text.ParagraphStyle other);
     property public final androidx.compose.ui.text.style.Hyphens? hyphens;
@@ -244,6 +251,7 @@
     property public final androidx.compose.ui.text.style.TextAlign? textAlign;
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
   }
 
   public final class ParagraphStyleKt {
@@ -321,13 +329,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SpanStyle {
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor public SpanStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -342,9 +357,12 @@
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle merge(optional androidx.compose.ui.text.SpanStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.SpanStyle plus(androidx.compose.ui.text.SpanStyle other);
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -455,6 +473,13 @@
     field public static final androidx.compose.ui.text.TextPainter INSTANCE;
   }
 
+  public final class TextPainterKt {
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, androidx.compose.ui.text.AnnotatedString text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, String text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, optional long color, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+  }
+
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TextRange {
     method public operator boolean contains(long other);
     method public operator boolean contains(int offset);
@@ -489,15 +514,22 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor public TextStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    method public androidx.compose.ui.text.TextStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+    method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -517,8 +549,10 @@
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.ParagraphStyle other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.TextStyle other);
@@ -526,9 +560,12 @@
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle toParagraphStyle();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle toSpanStyle();
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -548,6 +585,7 @@
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
     field public static final androidx.compose.ui.text.TextStyle.Companion Companion;
   }
 
@@ -1423,6 +1461,17 @@
     method public static androidx.compose.ui.text.style.TextIndent lerp(androidx.compose.ui.text.style.TextIndent start, androidx.compose.ui.text.style.TextIndent stop, float fraction);
   }
 
+  @androidx.compose.runtime.Immutable public final class TextMotion {
+    field public static final androidx.compose.ui.text.style.TextMotion.Companion Companion;
+  }
+
+  public static final class TextMotion.Companion {
+    method public androidx.compose.ui.text.style.TextMotion getAnimated();
+    method public androidx.compose.ui.text.style.TextMotion getStatic();
+    property public final androidx.compose.ui.text.style.TextMotion Animated;
+    property public final androidx.compose.ui.text.style.TextMotion Static;
+  }
+
   @kotlin.jvm.JvmInline public final value class TextOverflow {
     field public static final androidx.compose.ui.text.style.TextOverflow.Companion Companion;
   }
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index c7477c0..62308bf 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -141,9 +141,9 @@
     method public float getWidth();
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
-    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
-    method @androidx.compose.ui.text.ExperimentalTextApi public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
-    method @androidx.compose.ui.text.ExperimentalTextApi public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method @Deprecated public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
     property public final boolean didExceedMaxLines;
     property public final float firstBaseline;
     property public final float height;
@@ -201,8 +201,8 @@
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
     method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration);
-    method @androidx.compose.ui.text.ExperimentalTextApi public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
-    method @androidx.compose.ui.text.ExperimentalTextApi public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
     property public abstract boolean didExceedMaxLines;
     property public abstract float firstBaseline;
     property public abstract float height;
@@ -237,14 +237,14 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ParagraphStyle {
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
-    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+    method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
     method public androidx.compose.ui.text.style.Hyphens? getHyphens();
     method public androidx.compose.ui.text.style.LineBreak? getLineBreak();
     method public long getLineHeight();
@@ -253,7 +253,7 @@
     method public androidx.compose.ui.text.style.TextAlign? getTextAlign();
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.ParagraphStyle plus(androidx.compose.ui.text.ParagraphStyle other);
     property public final androidx.compose.ui.text.style.Hyphens? hyphens;
@@ -264,7 +264,7 @@
     property public final androidx.compose.ui.text.style.TextAlign? textAlign;
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.text.style.TextMotion? textMotion;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
   }
 
   public final class ParagraphStyleKt {
@@ -342,20 +342,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SpanStyle {
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public SpanStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.SpanStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
-    method @androidx.compose.ui.text.ExperimentalTextApi public float getAlpha();
+    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor public SpanStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.graphics.Brush? getBrush();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -370,12 +370,12 @@
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle merge(optional androidx.compose.ui.text.SpanStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.SpanStyle plus(androidx.compose.ui.text.SpanStyle other);
-    property @androidx.compose.ui.text.ExperimentalTextApi public final float alpha;
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.graphics.Brush? brush;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -487,10 +487,10 @@
   }
 
   public final class TextPainterKt {
-    method @androidx.compose.ui.text.ExperimentalTextApi public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, androidx.compose.ui.text.AnnotatedString text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long size, optional int blendMode);
-    method @androidx.compose.ui.text.ExperimentalTextApi public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, String text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long size, optional int blendMode);
-    method @androidx.compose.ui.text.ExperimentalTextApi public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, optional long color, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
-    method @androidx.compose.ui.text.ExperimentalTextApi public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, androidx.compose.ui.text.AnnotatedString text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, String text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, optional long color, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TextRange {
@@ -527,22 +527,22 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public TextStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor public TextStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.TextStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    method public androidx.compose.ui.text.TextStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method @androidx.compose.ui.text.ExperimentalTextApi public float getAlpha();
+    method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.graphics.Brush? getBrush();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -562,9 +562,10 @@
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
-    method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.ParagraphStyle other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.TextStyle other);
@@ -572,12 +573,12 @@
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle toParagraphStyle();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle toSpanStyle();
-    property @androidx.compose.ui.text.ExperimentalTextApi public final float alpha;
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.graphics.Brush? brush;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -597,7 +598,7 @@
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
-    property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.text.style.TextMotion? textMotion;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
     field public static final androidx.compose.ui.text.TextStyle.Companion Companion;
   }
 
@@ -1515,7 +1516,7 @@
     method public static androidx.compose.ui.text.style.TextIndent lerp(androidx.compose.ui.text.style.TextIndent start, androidx.compose.ui.text.style.TextIndent stop, float fraction);
   }
 
-  @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public final class TextMotion {
+  @androidx.compose.runtime.Immutable public final class TextMotion {
     field public static final androidx.compose.ui.text.style.TextMotion.Companion Companion;
   }
 
diff --git a/compose/ui/ui-text/api/restricted_current.ignore b/compose/ui/ui-text/api/restricted_current.ignore
index 7670599..bf842f8 100644
--- a/compose/ui/ui-text/api/restricted_current.ignore
+++ b/compose/ui/ui-text/api/restricted_current.ignore
@@ -1,4 +1,10 @@
 // Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.ui.text.Paragraph#paint(androidx.compose.ui.graphics.Canvas, androidx.compose.ui.graphics.Brush, float, androidx.compose.ui.graphics.Shadow, androidx.compose.ui.text.style.TextDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle, int):
+    Added method androidx.compose.ui.text.Paragraph.paint(androidx.compose.ui.graphics.Canvas,androidx.compose.ui.graphics.Brush,float,androidx.compose.ui.graphics.Shadow,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.graphics.drawscope.DrawStyle,int)
+AddedAbstractMethod: androidx.compose.ui.text.Paragraph#paint(androidx.compose.ui.graphics.Canvas, long, androidx.compose.ui.graphics.Shadow, androidx.compose.ui.text.style.TextDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle, int):
+    Added method androidx.compose.ui.text.Paragraph.paint(androidx.compose.ui.graphics.Canvas,long,androidx.compose.ui.graphics.Shadow,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.graphics.drawscope.DrawStyle,int)
+
+
 ChangedType: androidx.compose.ui.text.AnnotatedString.Builder#append(char):
     Method androidx.compose.ui.text.AnnotatedString.Builder.append has changed return type from androidx.compose.ui.text.AnnotatedString.Builder to void
 
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index f05e380..96355b0 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -128,7 +128,9 @@
     method public float getWidth();
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
-    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method @Deprecated public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
     property public final boolean didExceedMaxLines;
     property public final float firstBaseline;
     property public final float height;
@@ -186,6 +188,8 @@
     method public long getWordBoundary(int offset);
     method public boolean isLineEllipsized(int lineIndex);
     method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+    method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
     property public abstract boolean didExceedMaxLines;
     property public abstract float firstBaseline;
     property public abstract float height;
@@ -220,12 +224,14 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ParagraphStyle {
-    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+    method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
     method public androidx.compose.ui.text.style.Hyphens? getHyphens();
     method public androidx.compose.ui.text.style.LineBreak? getLineBreak();
     method public long getLineHeight();
@@ -234,6 +240,7 @@
     method public androidx.compose.ui.text.style.TextAlign? getTextAlign();
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.ParagraphStyle plus(androidx.compose.ui.text.ParagraphStyle other);
     property public final androidx.compose.ui.text.style.Hyphens? hyphens;
@@ -244,6 +251,7 @@
     property public final androidx.compose.ui.text.style.TextAlign? textAlign;
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
   }
 
   public final class ParagraphStyleKt {
@@ -321,13 +329,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SpanStyle {
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
-    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor public SpanStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method public androidx.compose.ui.text.SpanStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+    method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -342,9 +357,12 @@
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle merge(optional androidx.compose.ui.text.SpanStyle? other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.SpanStyle plus(androidx.compose.ui.text.SpanStyle other);
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -455,6 +473,13 @@
     field public static final androidx.compose.ui.text.TextPainter INSTANCE;
   }
 
+  public final class TextPainterKt {
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, androidx.compose.ui.text.AnnotatedString text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, String text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long size, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, optional long color, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+    method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+  }
+
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TextRange {
     method public operator boolean contains(long other);
     method public operator boolean contains(int offset);
@@ -489,15 +514,22 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    ctor public TextStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
-    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+    method public androidx.compose.ui.text.TextStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+    method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+    method public float getAlpha();
     method public long getBackground();
     method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+    method public androidx.compose.ui.graphics.Brush? getBrush();
     method public long getColor();
+    method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
     method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
     method public long getFontSize();
@@ -517,8 +549,10 @@
     method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.ParagraphStyle other);
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.TextStyle other);
@@ -526,9 +560,12 @@
     method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.SpanStyle other);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle toParagraphStyle();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle toSpanStyle();
+    property public final float alpha;
     property public final long background;
     property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+    property public final androidx.compose.ui.graphics.Brush? brush;
     property public final long color;
+    property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
     property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
     property public final String? fontFeatureSettings;
     property public final long fontSize;
@@ -548,6 +585,7 @@
     property public final androidx.compose.ui.text.style.TextDirection? textDirection;
     property public final androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform;
     property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+    property public final androidx.compose.ui.text.style.TextMotion? textMotion;
     field public static final androidx.compose.ui.text.TextStyle.Companion Companion;
   }
 
@@ -1423,6 +1461,17 @@
     method public static androidx.compose.ui.text.style.TextIndent lerp(androidx.compose.ui.text.style.TextIndent start, androidx.compose.ui.text.style.TextIndent stop, float fraction);
   }
 
+  @androidx.compose.runtime.Immutable public final class TextMotion {
+    field public static final androidx.compose.ui.text.style.TextMotion.Companion Companion;
+  }
+
+  public static final class TextMotion.Companion {
+    method public androidx.compose.ui.text.style.TextMotion getAnimated();
+    method public androidx.compose.ui.text.style.TextMotion getStatic();
+    property public final androidx.compose.ui.text.style.TextMotion Animated;
+    property public final androidx.compose.ui.text.style.TextMotion Static;
+  }
+
   @kotlin.jvm.JvmInline public final value class TextOverflow {
     field public static final androidx.compose.ui.text.style.TextOverflow.Companion Companion;
   }
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/TextMeasurerBenchmark.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/TextMeasurerBenchmark.kt
index 3a5ebaf..993ebf5 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/TextMeasurerBenchmark.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/TextMeasurerBenchmark.kt
@@ -25,7 +25,6 @@
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextMeasurer
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.drawText
@@ -44,7 +43,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
-@OptIn(ExperimentalTextApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
 class TextMeasurerBenchmark(
@@ -94,7 +92,6 @@
         return AnnotatedString(text = text, spanStyles = spanStyles)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun text_measurer_no_cache() {
         textBenchmarkRule.generator { textGenerator ->
@@ -115,7 +112,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun text_measurer_cached() {
         textBenchmarkRule.generator { textGenerator ->
@@ -136,7 +132,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun drawText_TextLayoutResult_no_change() {
         textBenchmarkRule.generator { textGenerator ->
@@ -168,7 +163,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun drawText_TextLayoutResult_color_override() {
         textBenchmarkRule.generator { textGenerator ->
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 e655120..2a7f037 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
@@ -27,14 +27,12 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.drawText
 import androidx.compose.ui.text.rememberTextMeasurer
 import androidx.compose.ui.unit.sp
 
-@OptIn(ExperimentalTextApi::class)
 @Sampled
 @Composable
 fun DrawTextLayoutResultSample() {
diff --git a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/SpanStyleSamples.kt b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/SpanStyleSamples.kt
index 88619248..15918fb 100644
--- a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/SpanStyleSamples.kt
+++ b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/SpanStyleSamples.kt
@@ -21,7 +21,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.withStyle
@@ -43,7 +42,6 @@
     )
 }
 
-@OptIn(ExperimentalTextApi::class)
 @Sampled
 @Composable
 fun SpanStyleBrushSample() {
diff --git a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextStyleSamples.kt b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextStyleSamples.kt
index 2742cdc..2950317 100644
--- a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextStyleSamples.kt
+++ b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextStyleSamples.kt
@@ -21,7 +21,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
@@ -48,7 +47,6 @@
     )
 }
 
-@OptIn(ExperimentalTextApi::class)
 @Sampled
 @Composable
 fun TextStyleBrushSample() {
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
index 0a5a040..386b478 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
@@ -175,7 +175,6 @@
         assertThat(paragraph.charSequence).hasSpanOnTop(ForegroundColorSpan::class, 0, "abc".length)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_setBrushOnWholeText() {
         val text = "abcde"
@@ -193,7 +192,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_setSolidColorBrushOnWholeText() {
         val text = "abcde"
@@ -209,7 +207,6 @@
         assertThat(paragraph.charSequence).hasSpan(ForegroundColorSpan::class, 0, text.length)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_setBrushOnPartOfText() {
         val text = "abcde"
@@ -227,7 +224,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_brushSpanReceivesSize() {
         with(defaultDensity) {
@@ -909,7 +905,6 @@
             }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_setDrawStyle() {
         val text = "abcde"
@@ -943,7 +938,6 @@
             }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testAnnotatedString_setDrawStyle_FillOnTopOfStroke() {
         val text = "abcde"
@@ -1348,7 +1342,6 @@
         assertThat(paragraph.textPaint.color).isEqualTo(color.toArgb())
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testSpanStyle_brush_appliedOnTextPaint() {
         val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) as ShaderBrush
@@ -1485,7 +1478,6 @@
         assertThat(paragraph.textPaint.isStrikeThruText).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testSpanStyle_drawStyle_stroke_appliedOnTextPaint() {
         val paragraph = simpleParagraph(
@@ -1506,7 +1498,6 @@
         assertThat(paragraph.textPaint.strokeJoin).isEqualTo(Paint.Join.BEVEL)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testSpanStyle_drawStyle_fill_appliedOnTextPaint() {
         val paragraph = simpleParagraph(
@@ -1582,7 +1573,6 @@
         assertThat(paragraph.textPaint.isUnderlineText).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testPaint_can_change_DrawStyle_to_Stroke() {
         val paragraph = simpleParagraph(
@@ -1603,7 +1593,6 @@
         assertThat(paragraph.textPaint.strokeJoin).isEqualTo(Paint.Join.BEVEL)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testPaint_can_change_DrawStyle_to_Fill() {
         val paragraph = simpleParagraph(
@@ -1619,7 +1608,6 @@
         assertThat(paragraph.textPaint.style).isEqualTo(Paint.Style.FILL)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testPaint_null_drawStyle_should_be_noop() {
         val paragraph = simpleParagraph(
@@ -1984,7 +1972,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun shaderBrushSpan_createsShaderOnlyOnce() {
         val fontSize = 20
@@ -2042,7 +2029,6 @@
         assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun textMotionStatic_setsCorrectFlagsOnTextPaint() {
         val textMotion = TextMotion.Static
@@ -2058,7 +2044,6 @@
         assertThat(paragraph.textPaint.hinting).isEqualTo(TextPaint.HINTING_ON)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun textMotionAnimated_setsCorrectFlagsOnTextPaint() {
         val textMotion = TextMotion.Animated
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/CacheTextLayoutInputTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/CacheTextLayoutInputTest.kt
index d377ab3..43d704c 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/CacheTextLayoutInputTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/CacheTextLayoutInputTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import androidx.compose.ui.geometry.Offset
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
index efe1944..10b709b 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
@@ -1512,7 +1512,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun multiParagraph_appliesBrush_toTheWholeText() = with(defaultDensity) {
         val fontSize = 20.sp
@@ -1550,7 +1549,6 @@
             .isEqualToBitmap(multiParagraph2.bitmap(brush))
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun multiParagraph_overridesAlphaDuringDraw() = with(defaultDensity) {
         val fontSize = 20.sp
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
index c2fe639..1b3119b 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
@@ -3740,7 +3740,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testDefaultSpanStyle_setBrush() {
         with(defaultDensity) {
@@ -3770,7 +3769,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testDefaultSpanStyle_setBrushAlpha() {
         with(defaultDensity) {
@@ -3802,7 +3800,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testDefaultSpanStyle_overrideAlphaDuringDraw() {
         with(defaultDensity) {
@@ -4366,7 +4363,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testSolidBrushColorIsSameAsColor() {
         with(defaultDensity) {
@@ -4393,7 +4389,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testSpanBrush_overridesDefaultBrush() {
         with(defaultDensity) {
@@ -4435,7 +4430,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun testBrush_notEffectedBy_TextDirection() {
         with(defaultDensity) {
@@ -4518,7 +4512,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun paint_withBlendMode_changesVisual() {
         with(defaultDensity) {
@@ -4554,7 +4547,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun paint_withBlendMode_sameResult() {
         with(defaultDensity) {
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformParagraphStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformParagraphStyleTest.kt
index b0feae1..09d8375 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformParagraphStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformParagraphStyleTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import com.google.common.truth.Truth.assertThat
@@ -23,7 +21,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@Suppress("DEPRECATION")
 @RunWith(JUnit4::class)
 class PlatformParagraphStyleTest {
 
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformSpanStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformSpanStyleTest.kt
index ae7baff..ef9afe5 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformSpanStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformSpanStyleTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import com.google.common.truth.Truth.assertThat
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformTextStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformTextStyleTest.kt
index a51ee71..8aa22e0 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformTextStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/PlatformTextStyleTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import com.google.common.truth.Truth.assertThat
@@ -23,7 +21,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@Suppress("DEPRECATION")
 @RunWith(JUnit4::class)
 class PlatformTextStyleTest {
 
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextLayoutCacheTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextLayoutCacheTest.kt
index f025c2f..14984f1 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextLayoutCacheTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextLayoutCacheTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import androidx.compose.ui.graphics.Brush
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
index 37667e3..dcbf9bb 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import androidx.compose.ui.graphics.Brush
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
index 3eb5285..af0ce0e3 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTextApi::class)
-
 package androidx.compose.ui.text
 
 import android.graphics.Bitmap
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
index d629400..102ce80a 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
@@ -73,7 +73,6 @@
     return bitmap
 }
 
-@OptIn(ExperimentalTextApi::class)
 fun Paragraph.bitmap(
     color: Color = Color.Unspecified,
     shadow: Shadow? = null,
@@ -93,7 +92,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 fun Paragraph.bitmap(
     brush: Brush,
     alpha: Float,
@@ -123,7 +121,6 @@
  * We have to re-specify the brush during paint(draw) to apply it according to the total size of
  * MultiParagraph.
  */
-@OptIn(ExperimentalTextApi::class)
 fun MultiParagraph.bitmap(
     brush: Brush? = null,
     alpha: Float = Float.NaN,
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
index 26b1954..b6168c47 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
@@ -102,7 +102,6 @@
         fontLoader.cacheKey
     )
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun onResolve_onlyBlockingFonts_doesNotLoad() {
         val expected = Typeface.MONOSPACE
@@ -117,7 +116,6 @@
         assertThat(result).isImmutableTypefaceOf(expected)
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun onResolve_blockingAndAsyncFonts_matchesBlocking_doesLoad() {
         val expected = Typeface.MONOSPACE
@@ -365,7 +363,6 @@
         )
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun onResolve_optionalAndAsyncFonts_matchesOptional_doesLoad() {
         val expected = Typeface.MONOSPACE
@@ -470,7 +467,6 @@
         scope.advanceUntilIdle()
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun onResolve_optionalAndAsyncAndBlockingFonts_matchesOptional_doesNotLoadBlockingAsync() {
         val asyncFont = AsyncFauxFont(typefaceLoader)
@@ -689,7 +685,6 @@
         requestAndCompleteOnRealDispatcher()
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun runtimeExceptionOnRealDispatcher_informsExceptionHandler() {
         val exception: CompletableDeferred<Throwable> = CompletableDeferred()
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
index 0c3455e..9f24373 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.AndroidFont
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontLoadingStrategy.Companion.Async
@@ -156,7 +155,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 class BlockingFauxFont(
     typefaceLoader: AsyncTestTypefaceLoader,
     internal val typeface: Typeface,
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/SpannableExtensionsTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/SpannableExtensionsTest.kt
index 576b5339..2ec8bcb 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/SpannableExtensionsTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/SpannableExtensionsTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontStyle
@@ -37,6 +36,9 @@
 import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argThat
 import org.mockito.kotlin.eq
@@ -45,9 +47,6 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -505,7 +504,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun shaderBrush_shouldAdd_shaderBrushSpan_whenApplied() {
         val text = "abcde abcde"
@@ -524,7 +522,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun shaderBrush_shouldAdd_shaderBrushSpan_whenApplied_withSpecifiedAlpha() {
         val text = "abcde abcde"
@@ -543,7 +540,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun solidColorBrush_shouldAdd_ForegroundColorSpan_whenApplied() {
         val text = "abcde abcde"
@@ -557,7 +553,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun whenColorAndShaderBrushSpansCollide_bothShouldApply() {
         val text = "abcde abcde"
@@ -581,7 +576,6 @@
         assertThat(spannable).hasSpan(ForegroundColorSpan::class, 0, text.length)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun whenColorAndSolidColorBrushSpansCollide_bothShouldApply() {
         val text = "abcde abcde"
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextPaintExtensionsTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextPaintExtensionsTest.kt
index 70890ed..b7cdc5e3 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextPaintExtensionsTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextPaintExtensionsTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.toArgb
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
@@ -355,7 +354,6 @@
         assertThat(notApplied?.color).isEqualTo(Color.Unspecified)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forLinearAndSubpixel() {
         val textMotion = TextMotion(TextMotion.Linearity.Linear, true)
@@ -369,7 +367,6 @@
         assertThat(tp.hinting).isEqualTo(TextPaint.HINTING_OFF)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forFontHintingAndSubpixel() {
         val textMotion = TextMotion(TextMotion.Linearity.FontHinting, true)
@@ -383,7 +380,6 @@
         assertThat(tp.hinting).isEqualTo(TextPaint.HINTING_ON)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forNoneAndSubpixel() {
         val textMotion = TextMotion(TextMotion.Linearity.None, true)
@@ -397,7 +393,6 @@
         assertThat(tp.hinting).isEqualTo(TextPaint.HINTING_OFF)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forLinear() {
         val textMotion = TextMotion(TextMotion.Linearity.Linear, false)
@@ -409,7 +404,6 @@
         assertThat(tp.hinting).isEqualTo(TextPaint.HINTING_OFF)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forFontHinting() {
         val textMotion = TextMotion(TextMotion.Linearity.FontHinting, false)
@@ -421,7 +415,6 @@
         assertThat(tp.hinting).isEqualTo(TextPaint.HINTING_ON)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun setTextMotion_setsCorrectFlags_forNone() {
         val textMotion = TextMotion(TextMotion.Linearity.None, false)
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
index 3aff906..35b572e 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
@@ -459,7 +459,6 @@
         paint(canvas)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun paint(
         canvas: Canvas,
         color: Color,
@@ -482,7 +481,6 @@
         textPaint.blendMode = currBlendMode
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
index 467355f..3744926 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
@@ -131,6 +131,8 @@
      * This configuration was added for migration of the apps in case some code or design  was
      * relying includeFontPadding=true behavior.
      */
+    @Suppress("GetterSetterNames")
+    @get:Suppress("GetterSetterNames")
     val includeFontPadding: Boolean
 
     /**
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/Paragraph.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/Paragraph.android.kt
index 7bbfe8d..83ba3cb 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/Paragraph.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/Paragraph.android.kt
@@ -58,7 +58,6 @@
     actual fun getBoundingBox(offset: Int): Rect
     actual fun getWordBoundary(offset: Int): TextRange
     actual fun paint(canvas: Canvas, color: Color, shadow: Shadow?, textDecoration: TextDecoration?)
-    @ExperimentalTextApi
     actual fun paint(
         canvas: Canvas,
         color: Color,
@@ -67,7 +66,6 @@
         drawStyle: DrawStyle?,
         blendMode: BlendMode
     )
-    @ExperimentalTextApi
     actual fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.kt
index e88267d..0b86686 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.kt
@@ -25,12 +25,10 @@
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.drawscope.DrawStyle
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.MultiParagraph
 import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.util.fastForEach
 
-@OptIn(ExperimentalTextApi::class)
 internal actual fun MultiParagraph.drawMultiParagraph(
     canvas: Canvas,
     brush: Brush,
@@ -80,7 +78,6 @@
     canvas.restore()
 }
 
-@OptIn(ExperimentalTextApi::class)
 private fun MultiParagraph.drawParagraphs(
     canvas: Canvas,
     brush: Brush,
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
index 3bcd60e..2fd7809 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
@@ -23,7 +23,6 @@
 import android.text.style.CharacterStyle
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.DefaultIncludeFontPadding
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextStyle
@@ -44,7 +43,7 @@
 import androidx.compose.ui.unit.isUnspecified
 import androidx.emoji2.text.EmojiCompat
 
-@OptIn(InternalPlatformTextApi::class, ExperimentalTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 internal fun createCharSequence(
     text: String,
     contextFontSize: Float,
@@ -118,8 +117,6 @@
     return spannableString
 }
 
-@OptIn(ExperimentalTextApi::class)
-@Suppress("DEPRECATION")
 internal fun TextStyle.isIncludeFontPaddingEnabled(): Boolean {
     return platformStyle?.paragraphStyle?.includeFontPadding ?: DefaultIncludeFontPadding
 }
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt
index 3b316c8..972b324 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt
@@ -215,7 +215,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 private fun Spannable.setSpanStyle(
     spanStyleRange: AnnotatedString.Range<SpanStyle>,
     density: Density
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
index b92b9c55..ee6daab 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
@@ -21,7 +21,6 @@
 import android.text.TextPaint
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
@@ -44,7 +43,6 @@
  * regular platform spans such as background, baselineShift. This function also returns a new
  * SpanStyle that consists of attributes that were not applied to the TextPaint.
  */
-@OptIn(ExperimentalTextApi::class)
 internal fun AndroidTextPaint.applySpanStyle(
     style: SpanStyle,
     resolveTypeface: (FontFamily?, FontWeight, FontStyle, FontSynthesis) -> Typeface,
@@ -154,7 +152,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 internal fun AndroidTextPaint.setTextMotion(textMotion: TextMotion?) {
     val finalTextMotion = textMotion ?: TextMotion.Static
     flags = if (finalTextMotion.subpixelTextPositioning) {
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt
index e4434ce..c508974 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt
@@ -17,12 +17,10 @@
 package androidx.compose.ui.text.style
 
 import androidx.compose.runtime.Immutable
-import androidx.compose.ui.text.ExperimentalTextApi
 
 /**
  * Implementation of possible TextMotion configurations on Android.
  */
-@ExperimentalTextApi
 @Immutable
 actual class TextMotion internal constructor(
     internal val linearity: Linearity,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt
index dc32ce8..c0fb6f3 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt
@@ -385,6 +385,10 @@
     }
 
     /** Paint the paragraphs to canvas. */
+    @Deprecated(
+        "Use the new paint function that takes canvas as the only required parameter.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun paint(
         canvas: Canvas,
         color: Color = Color.Unspecified,
@@ -400,7 +404,6 @@
     }
 
     /** Paint the paragraphs to canvas. */
-    @ExperimentalTextApi
     fun paint(
         canvas: Canvas,
         color: Color = Color.Unspecified,
@@ -418,7 +421,6 @@
     }
 
     /** Paint the paragraphs to canvas. */
-    @ExperimentalTextApi
     fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt
index 9b24ca1..b3653ef 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt
@@ -254,6 +254,10 @@
      * TextDecoration on this paragraph, `null` does not change the currently set [TextDecoration]
      * configuration.
      */
+    @Deprecated(
+        "Use the new paint function that takes canvas as the only required parameter.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun paint(
         canvas: Canvas,
         color: Color = Color.Unspecified,
@@ -281,7 +285,6 @@
      * currently set DrawStyle.
      * @param blendMode Blending algorithm to be applied to the Paragraph while painting.
      */
-    @ExperimentalTextApi
     fun paint(
         canvas: Canvas,
         color: Color = Color.Unspecified,
@@ -316,7 +319,6 @@
      * currently set DrawStyle.
      * @param blendMode Blending algorithm to be applied to the Paragraph while painting.
      */
-    @ExperimentalTextApi
     fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
index f97de62..aea5a76 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isSpecified
 import androidx.compose.ui.unit.isUnspecified
 
 private val DefaultLineHeight = TextUnit.Unspecified
@@ -62,7 +63,7 @@
  * @see TextStyle
  */
 @Immutable
-class ParagraphStyle @ExperimentalTextApi constructor(
+class ParagraphStyle constructor(
     val textAlign: TextAlign? = null,
     val textDirection: TextDirection? = null,
     val lineHeight: TextUnit = TextUnit.Unspecified,
@@ -71,9 +72,6 @@
     val lineHeightStyle: LineHeightStyle? = null,
     val lineBreak: LineBreak? = null,
     val hyphens: Hyphens? = null,
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
-    @property:ExperimentalTextApi
     val textMotion: TextMotion? = null
 ) {
 
@@ -89,7 +87,6 @@
             "constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     constructor(
         textAlign: TextAlign? = null,
         textDirection: TextDirection? = null,
@@ -113,7 +110,6 @@
             "constructors.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     constructor(
         textAlign: TextAlign? = null,
         textDirection: TextDirection? = null,
@@ -133,35 +129,12 @@
         textMotion = null
     )
 
-    /**
-     * Paragraph styling configuration for a paragraph. The difference between [SpanStyle] and
-     * `ParagraphStyle` is that, `ParagraphStyle` can be applied to a whole [Paragraph] while
-     * [SpanStyle] can be applied at the character level.
-     * Once a portion of the text is marked with a `ParagraphStyle`, that portion will be separated from
-     * the remaining as if a line feed character was added.
-     *
-     * @sample androidx.compose.ui.text.samples.ParagraphStyleSample
-     * @sample androidx.compose.ui.text.samples.ParagraphStyleAnnotatedStringsSample
-     *
-     * @param textAlign The alignment of the text within the lines of the paragraph.
-     * @param textDirection The algorithm to be used to resolve the final text direction:
-     * Left To Right or Right To Left.
-     * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM.
-     * @param textIndent The indentation of the paragraph.
-     * @param platformStyle Platform specific [ParagraphStyle] parameters.
-     * @param lineHeightStyle the configuration for line height such as vertical alignment of the
-     * line, whether to apply additional space as a result of line height to top of first line top and
-     * bottom of last line. The configuration is applied only when a [lineHeight] is defined.
-     * When null, [LineHeightStyle.Default] is used.
-     * @param lineBreak The line breaking configuration for the text.
-     * @param hyphens The configuration of hyphenation.
-     *
-     * @see Paragraph
-     * @see AnnotatedString
-     * @see SpanStyle
-     * @see TextStyle
-     */
-    @OptIn(ExperimentalTextApi::class)
+    @Deprecated(
+        "ParagraphStyle constructors that do not take new stable parameters " +
+            "like LineBreak, Hyphens, TextMotion are deprecated. Please use the new stable " +
+            "constructors.",
+        level = DeprecationLevel.HIDDEN
+    )
     constructor(
         textAlign: TextAlign? = null,
         textDirection: TextDirection? = null,
@@ -198,34 +171,23 @@
      *
      * If the given paragraph style is null, returns this paragraph style.
      */
-    @OptIn(ExperimentalTextApi::class)
     @Stable
     fun merge(other: ParagraphStyle? = null): ParagraphStyle {
         if (other == null) return this
 
-        return ParagraphStyle(
-            lineHeight = if (other.lineHeight.isUnspecified) {
-                this.lineHeight
-            } else {
-                other.lineHeight
-            },
-            textIndent = other.textIndent ?: this.textIndent,
-            textAlign = other.textAlign ?: this.textAlign,
-            textDirection = other.textDirection ?: this.textDirection,
-            platformStyle = mergePlatformStyle(other.platformStyle),
-            lineHeightStyle = other.lineHeightStyle ?: this.lineHeightStyle,
-            lineBreak = other.lineBreak ?: this.lineBreak,
-            hyphens = other.hyphens ?: this.hyphens,
-            textMotion = other.textMotion ?: this.textMotion
+        return fastMerge(
+            textAlign = other.textAlign,
+            textDirection = other.textDirection,
+            lineHeight = other.lineHeight,
+            textIndent = other.textIndent,
+            platformStyle = other.platformStyle,
+            lineHeightStyle = other.lineHeightStyle,
+            lineBreak = other.lineBreak,
+            hyphens = other.hyphens,
+            textMotion = other.textMotion
         )
     }
 
-    private fun mergePlatformStyle(other: PlatformParagraphStyle?): PlatformParagraphStyle? {
-        if (platformStyle == null) return other
-        if (other == null) return platformStyle
-        return platformStyle.merge(other)
-    }
-
     /**
      * Plus operator overload that applies a [merge].
      */
@@ -238,7 +200,6 @@
             "copy constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     fun copy(
         textAlign: TextAlign? = this.textAlign,
         textDirection: TextDirection? = this.textDirection,
@@ -264,7 +225,6 @@
             "copy constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     fun copy(
         textAlign: TextAlign? = this.textAlign,
         textDirection: TextDirection? = this.textDirection,
@@ -286,7 +246,12 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
+    @Deprecated(
+        "ParagraphStyle copy constructors that do not take new stable parameters " +
+            "like LineBreak, Hyphens, TextMotion are deprecated. Please use the new stable " +
+            "copy constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun copy(
         textAlign: TextAlign? = this.textAlign,
         textDirection: TextDirection? = this.textDirection,
@@ -310,7 +275,6 @@
         )
     }
 
-    @ExperimentalTextApi
     fun copy(
         textAlign: TextAlign? = this.textAlign,
         textDirection: TextDirection? = this.textDirection,
@@ -335,7 +299,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is ParagraphStyle) return false
@@ -353,7 +316,6 @@
         return true
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun hashCode(): Int {
         var result = textAlign?.hashCode() ?: 0
         result = 31 * result + (textDirection?.hashCode() ?: 0)
@@ -367,7 +329,6 @@
         return result
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun toString(): String {
         return "ParagraphStyle(" +
             "textAlign=$textAlign, " +
@@ -396,7 +357,6 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
-@OptIn(ExperimentalTextApi::class)
 @Stable
 fun lerp(start: ParagraphStyle, stop: ParagraphStyle, fraction: Float): ParagraphStyle {
     return ParagraphStyle(
@@ -435,7 +395,6 @@
     return lerp(startNonNull, stopNonNull, fraction)
 }
 
-@OptIn(ExperimentalTextApi::class)
 internal fun resolveParagraphStyleDefaults(
     style: ParagraphStyle,
     direction: LayoutDirection
@@ -450,3 +409,61 @@
     hyphens = style.hyphensOrDefault,
     textMotion = style.textMotion ?: TextMotion.Static
 )
+
+ @OptIn(ExperimentalTextApi::class)
+ internal fun ParagraphStyle.fastMerge(
+    textAlign: TextAlign?,
+    textDirection: TextDirection?,
+    lineHeight: TextUnit,
+    textIndent: TextIndent?,
+    platformStyle: PlatformParagraphStyle?,
+    lineHeightStyle: LineHeightStyle?,
+    lineBreak: LineBreak?,
+    hyphens: Hyphens?,
+    textMotion: TextMotion?
+): ParagraphStyle {
+     // prioritize the parameters to Text in diffs here
+     /**
+      *  textAlign: TextAlign?
+      *  lineHeight: TextUnit
+      */
+
+     // any new vals should do a pre-merge check here
+     val requiresAlloc = textAlign != null && textAlign != this.textAlign ||
+         lineHeight.isSpecified && lineHeight != this.lineHeight ||
+         textIndent != null && textIndent != this.textIndent ||
+         textDirection != null && textDirection != this.textDirection ||
+         platformStyle != null && platformStyle != this.platformStyle ||
+         lineHeightStyle != null && lineHeightStyle != this.lineHeightStyle ||
+         lineBreak != null && lineBreak != this.lineBreak ||
+         hyphens != null && hyphens != this.hyphens ||
+         textMotion != null && textMotion != this.textMotion
+
+     if (!requiresAlloc) {
+         return this
+     }
+
+     return ParagraphStyle(
+         lineHeight = if (lineHeight.isUnspecified) {
+             this.lineHeight
+         } else {
+             lineHeight
+         },
+         textIndent = textIndent ?: this.textIndent,
+         textAlign = textAlign ?: this.textAlign,
+         textDirection = textDirection ?: this.textDirection,
+         platformStyle = mergePlatformStyle(platformStyle),
+         lineHeightStyle = lineHeightStyle ?: this.lineHeightStyle,
+         lineBreak = lineBreak ?: this.lineBreak,
+         hyphens = hyphens ?: this.hyphens,
+         textMotion = textMotion ?: this.textMotion
+     )
+}
+
+private fun ParagraphStyle.mergePlatformStyle(
+    other: PlatformParagraphStyle?
+): PlatformParagraphStyle? {
+    if (platformStyle == null) return other
+    if (other == null) return platformStyle
+    return platformStyle.merge(other)
+}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
index aafe77a..f024207 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.drawscope.DrawStyle
 import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.isSpecified
 import androidx.compose.ui.graphics.lerp
 import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.font.FontFamily
@@ -37,6 +38,7 @@
 import androidx.compose.ui.text.style.TextGeometricTransform
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isSpecified
 import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.lerp
 import androidx.compose.ui.unit.sp
@@ -101,9 +103,7 @@
     val textDecoration: TextDecoration? = null,
     val shadow: Shadow? = null,
     val platformStyle: PlatformSpanStyle? = null,
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @property:ExperimentalTextApi
-    @get:ExperimentalTextApi val drawStyle: DrawStyle? = null
+    val drawStyle: DrawStyle? = null
 ) {
 
     /**
@@ -138,6 +138,12 @@
      * @see TextStyle
      * @see ParagraphStyle
      */
+    @Deprecated(
+        "SpanStyle constructors that do not take new stable parameters " +
+            "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
+            "constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -204,6 +210,12 @@
      * @see TextStyle
      * @see ParagraphStyle
      */
+    @Deprecated(
+        "SpanStyle constructors that do not take new stable parameters " +
+            "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
+            "constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -273,7 +285,6 @@
      * @see TextStyle
      * @see ParagraphStyle
      */
-    @ExperimentalTextApi
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -349,7 +360,6 @@
      * @see TextStyle
      * @see ParagraphStyle
      */
-    @ExperimentalTextApi
     constructor(
         brush: Brush?,
         alpha: Float = Float.NaN,
@@ -395,18 +405,12 @@
     /**
      * Brush to draw text. If not null, overrides [color].
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val brush: Brush? get() = this.textForegroundStyle.brush
 
     /**
      * Opacity of text. This value is either provided along side Brush, or via alpha channel in
      * color.
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val alpha: Float get() = this.textForegroundStyle.alpha
 
     /**
@@ -418,48 +422,43 @@
      *
      * If the given span style is null, returns this span style.
      */
-    @OptIn(ExperimentalTextApi::class)
     @Stable
     fun merge(other: SpanStyle? = null): SpanStyle {
         if (other == null) return this
-
-        return SpanStyle(
-            textForegroundStyle = textForegroundStyle.merge(other.textForegroundStyle),
-            fontFamily = other.fontFamily ?: this.fontFamily,
-            fontSize = if (!other.fontSize.isUnspecified) other.fontSize else this.fontSize,
-            fontWeight = other.fontWeight ?: this.fontWeight,
-            fontStyle = other.fontStyle ?: this.fontStyle,
-            fontSynthesis = other.fontSynthesis ?: this.fontSynthesis,
-            fontFeatureSettings = other.fontFeatureSettings ?: this.fontFeatureSettings,
-            letterSpacing = if (!other.letterSpacing.isUnspecified) {
-                other.letterSpacing
-            } else {
-                this.letterSpacing
-            },
-            baselineShift = other.baselineShift ?: this.baselineShift,
-            textGeometricTransform = other.textGeometricTransform ?: this.textGeometricTransform,
-            localeList = other.localeList ?: this.localeList,
-            background = other.background.takeOrElse { this.background },
-            textDecoration = other.textDecoration ?: this.textDecoration,
-            shadow = other.shadow ?: this.shadow,
-            platformStyle = mergePlatformStyle(other.platformStyle),
-            drawStyle = other.drawStyle ?: this.drawStyle
+        return fastMerge(
+            color = other.textForegroundStyle.color,
+            brush = other.textForegroundStyle.brush,
+            alpha = other.textForegroundStyle.alpha,
+            fontSize = other.fontSize,
+            fontWeight = other.fontWeight,
+            fontStyle = other.fontStyle,
+            fontSynthesis = other.fontSynthesis,
+            fontFamily = other.fontFamily,
+            fontFeatureSettings = other.fontFeatureSettings,
+            letterSpacing = other.letterSpacing,
+            baselineShift = other.baselineShift,
+            textGeometricTransform = other.textGeometricTransform,
+            localeList = other.localeList,
+            background = other.background,
+            textDecoration = other.textDecoration,
+            shadow = other.shadow,
+            platformStyle = other.platformStyle,
+            drawStyle = other.drawStyle
         )
     }
 
-    private fun mergePlatformStyle(other: PlatformSpanStyle?): PlatformSpanStyle? {
-        if (platformStyle == null) return other
-        if (other == null) return platformStyle
-        return platformStyle.merge(other)
-    }
-
     /**
      * Plus operator overload that applies a [merge].
      */
     @Stable
     operator fun plus(other: SpanStyle): SpanStyle = this.merge(other)
 
-    @OptIn(ExperimentalTextApi::class)
+    @Deprecated(
+        "SpanStyle copy constructors that do not take new stable parameters " +
+            "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
+            "copy constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun copy(
         color: Color = this.color,
         fontSize: TextUnit = this.fontSize,
@@ -500,6 +499,12 @@
         )
     }
 
+    @Deprecated(
+        "SpanStyle copy constructors that do not take new stable parameters " +
+            "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
+            "copy constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun copy(
         color: Color = this.color,
         fontSize: TextUnit = this.fontSize,
@@ -540,7 +545,6 @@
         )
     }
 
-    @ExperimentalTextApi
     fun copy(
         color: Color = this.color,
         fontSize: TextUnit = this.fontSize,
@@ -583,7 +587,6 @@
         )
     }
 
-    @ExperimentalTextApi
     fun copy(
         brush: Brush?,
         alpha: Float = this.alpha,
@@ -647,7 +650,6 @@
         return true
     }
 
-    @OptIn(ExperimentalTextApi::class)
     private fun hasSameNonLayoutAttributes(other: SpanStyle): Boolean {
         if (textForegroundStyle != other.textForegroundStyle) return false
         if (textDecoration != other.textDecoration) return false
@@ -656,7 +658,6 @@
         return true
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun hashCode(): Int {
         var result = color.hashCode()
         result = 31 * result + brush.hashCode()
@@ -695,7 +696,6 @@
         return result
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun toString(): String {
         return "SpanStyle(" +
             "color=$color, " +
@@ -748,7 +748,6 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
-@OptIn(ExperimentalTextApi::class)
 fun lerp(start: SpanStyle, stop: SpanStyle, fraction: Float): SpanStyle {
     return SpanStyle(
         textForegroundStyle = lerp(start.textForegroundStyle, stop.textForegroundStyle, fraction),
@@ -829,7 +828,6 @@
     return lerp(startNonNull, stopNonNull, fraction)
 }
 
-@OptIn(ExperimentalTextApi::class)
 internal fun resolveSpanStyleDefaults(style: SpanStyle) = SpanStyle(
     textForegroundStyle = style.textForegroundStyle.takeOrElse {
         TextForegroundStyle.from(DefaultColor)
@@ -853,4 +851,101 @@
     shadow = style.shadow ?: Shadow.None,
     platformStyle = style.platformStyle,
     drawStyle = style.drawStyle ?: Fill
-)
\ No newline at end of file
+)
+
+@OptIn(ExperimentalTextApi::class)
+internal fun SpanStyle.fastMerge(
+    color: Color,
+    brush: Brush?,
+    alpha: Float,
+    fontSize: TextUnit,
+    fontWeight: FontWeight?,
+    fontStyle: FontStyle?,
+    fontSynthesis: FontSynthesis?,
+    fontFamily: FontFamily?,
+    fontFeatureSettings: String?,
+    letterSpacing: TextUnit,
+    baselineShift: BaselineShift?,
+    textGeometricTransform: TextGeometricTransform?,
+    localeList: LocaleList?,
+    background: Color,
+    textDecoration: TextDecoration?,
+    shadow: Shadow?,
+    platformStyle: PlatformSpanStyle?,
+    drawStyle: DrawStyle?
+): SpanStyle {
+    // prioritize the parameters to Text in diffs here
+    /**
+     *  color: Color
+     *  fontSize: TextUnit
+     *  fontStyle: FontStyle?
+     *  fontWeight: FontWeight?
+     *  fontFamily: FontFamily?
+     *  letterSpacing: TextUnit
+     *  textDecoration: TextDecoration?
+     *  textAlign: TextAlign?
+     *  lineHeight: TextUnit
+     */
+
+    // any new vals should do a pre-merge check here
+    val requiresAlloc = fontSize.isSpecified && fontSize != this.fontSize ||
+        brush == null && color != textForegroundStyle.color ||
+        fontStyle != null && fontStyle != this.fontStyle ||
+        fontWeight != null && fontWeight != this.fontWeight ||
+        // ref check for font-family, since we don't want to compare lists in fast path
+        fontFamily != null && fontFamily !== this.fontFamily ||
+        letterSpacing.isSpecified && letterSpacing != this.letterSpacing ||
+        textDecoration != null && textDecoration != this.textDecoration ||
+        // then compare the remaining params, for potential non-Text merges
+        brush != textForegroundStyle.brush ||
+        brush != null && alpha != this.textForegroundStyle.alpha ||
+        fontSynthesis != null && fontSynthesis != this.fontSynthesis ||
+        fontFeatureSettings != null && fontFeatureSettings != this.fontFeatureSettings ||
+        baselineShift != null && baselineShift != this.baselineShift ||
+        textGeometricTransform != null && textGeometricTransform != this.textGeometricTransform ||
+        localeList != null && localeList != this.localeList ||
+        background.isSpecified && background != this.background ||
+        shadow != null && shadow != this.shadow ||
+        platformStyle != null && platformStyle != this.platformStyle ||
+        drawStyle != null && drawStyle != this.drawStyle
+
+    if (!requiresAlloc) {
+        // we're done
+        return this
+    }
+
+    val otherTextForegroundStyle = if (brush != null) {
+        TextForegroundStyle.from(brush, alpha)
+    } else {
+        TextForegroundStyle.from(color)
+    }
+
+    return SpanStyle(
+        textForegroundStyle = textForegroundStyle.merge(otherTextForegroundStyle),
+        fontFamily = fontFamily ?: this.fontFamily,
+        fontSize = if (!fontSize.isUnspecified) fontSize else this.fontSize,
+        fontWeight = fontWeight ?: this.fontWeight,
+        fontStyle = fontStyle ?: this.fontStyle,
+        fontSynthesis = fontSynthesis ?: this.fontSynthesis,
+        fontFeatureSettings = fontFeatureSettings ?: this.fontFeatureSettings,
+        letterSpacing = if (!letterSpacing.isUnspecified) {
+            letterSpacing
+        } else {
+            this.letterSpacing
+        },
+        baselineShift = baselineShift ?: this.baselineShift,
+        textGeometricTransform = textGeometricTransform ?: this.textGeometricTransform,
+        localeList = localeList ?: this.localeList,
+        background = background.takeOrElse { this.background },
+        textDecoration = textDecoration ?: this.textDecoration,
+        shadow = shadow ?: this.shadow,
+        platformStyle = mergePlatformStyle(platformStyle),
+        drawStyle = drawStyle ?: this.drawStyle
+    )
+}
+
+private fun SpanStyle.mergePlatformStyle(other: PlatformSpanStyle?): PlatformSpanStyle? {
+    if (platformStyle == null) return other
+    if (other == null) return platformStyle
+    return platformStyle.merge(other)
+}
\ No newline at end of file
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 44c34a2..f9aa838 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
@@ -40,8 +40,6 @@
 import kotlin.math.ceil
 import kotlin.math.roundToInt
 
-internal val DefaultTextBlendMode = BlendMode.SrcOver
-
 object TextPainter {
 
     // TODO(b/236964276): Deprecate when TextMeasurer and drawText are no longer Experimental
@@ -51,7 +49,6 @@
      * @param canvas a canvas to be drawn
      * @param textLayoutResult a result of text layout
      */
-    @OptIn(ExperimentalTextApi::class)
     fun paint(canvas: Canvas, textLayoutResult: TextLayoutResult) {
         val needClipping = textLayoutResult.hasVisualOverflow &&
             textLayoutResult.layoutInput.overflow != TextOverflow.Visible
@@ -143,7 +140,6 @@
  *
  * @see TextMeasurer
  */
-@ExperimentalTextApi
 fun DrawScope.drawText(
     textMeasurer: TextMeasurer,
     text: AnnotatedString,
@@ -211,7 +207,6 @@
  *
  * @see TextMeasurer
  */
-@ExperimentalTextApi
 fun DrawScope.drawText(
     textMeasurer: TextMeasurer,
     text: String,
@@ -263,7 +258,6 @@
  *
  * @sample androidx.compose.ui.text.samples.DrawTextLayoutResultSample
  */
-@ExperimentalTextApi
 fun DrawScope.drawText(
     textLayoutResult: TextLayoutResult,
     color: Color = Color.Unspecified,
@@ -326,7 +320,6 @@
  *
  * @sample androidx.compose.ui.text.samples.DrawTextLayoutResultSample
  */
-@ExperimentalTextApi
 fun DrawScope.drawText(
     textLayoutResult: TextLayoutResult,
     brush: Brush,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
index ca8e2c9..1b715f9 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
@@ -78,7 +78,6 @@
             "constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -137,7 +136,6 @@
             "constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -229,6 +227,12 @@
      * @param lineBreak The line breaking configuration for the text.
      * @param hyphens The configuration of hyphenation.
      */
+    @Deprecated(
+        "TextStyle constructors that do not take new stable parameters " +
+            "like TextMotion are deprecated. Please use the new stable " +
+            "constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -323,7 +327,6 @@
      * @param hyphens The configuration of hyphenation.
      * @param textMotion Text character placement, whether to optimize for animated or static text.
      */
-    @ExperimentalTextApi
     constructor(
         color: Color = Color.Unspecified,
         fontSize: TextUnit = TextUnit.Unspecified,
@@ -425,7 +428,6 @@
      * @param hyphens The configuration of hyphenation.
      * @param textMotion Text character placement, whether to optimize for animated or static text.
      */
-    @ExperimentalTextApi
     constructor(
         brush: Brush?,
         alpha: Float = Float.NaN,
@@ -511,6 +513,102 @@
     }
 
     /**
+     * Fast merge non-default values and parameters.
+     *
+     * This is the same algorithm as [merge] but does not require allocating a [TextStyle] to call.
+     *
+     * This is a similar algorithm to [copy] but when either this or a parameter are set to a
+     * default value, the other value will take precedent.
+     *
+     * To explain better, consider the following examples:
+     *
+     * Example 1:
+     * - this.color = [Color.Unspecified]
+     * - [color] = [Color.Red]
+     * - result => [Color.Red]
+     *
+     * Example 2:
+     * - this.color = [Color.Red]
+     * - [color] = [Color.Unspecified]
+     * - result => [Color.Red]
+     *
+     * Example 3:
+     * - this.color = [Color.Red]
+     * - [color] = [Color.Blue]
+     * - result => [Color.Blue]]
+     *
+     * You should _always_ use this method over the [merge]([TextStyle]) overload when you do not
+     * already have a TextStyle allocated. You should chose this over [copy] when building a theming
+     * system and applying styling information to a specific usage.
+     *
+     * @return this or a new TextLayoutResult with all parameters chosen to the non-default option
+     * provided.
+     *
+     * @see merge
+     */
+    @Stable
+    fun merge(
+        color: Color = Color.Unspecified,
+        fontSize: TextUnit = TextUnit.Unspecified,
+        fontWeight: FontWeight? = null,
+        fontStyle: FontStyle? = null,
+        fontSynthesis: FontSynthesis? = null,
+        fontFamily: FontFamily? = null,
+        fontFeatureSettings: String? = null,
+        letterSpacing: TextUnit = TextUnit.Unspecified,
+        baselineShift: BaselineShift? = null,
+        textGeometricTransform: TextGeometricTransform? = null,
+        localeList: LocaleList? = null,
+        background: Color = Color.Unspecified,
+        textDecoration: TextDecoration? = null,
+        shadow: Shadow? = null,
+        drawStyle: DrawStyle? = null,
+        textAlign: TextAlign? = null,
+        textDirection: TextDirection? = null,
+        lineHeight: TextUnit = TextUnit.Unspecified,
+        textIndent: TextIndent? = null,
+        lineHeightStyle: LineHeightStyle? = null,
+        lineBreak: LineBreak? = null,
+        hyphens: Hyphens? = null,
+        platformStyle: PlatformTextStyle? = null,
+        textMotion: TextMotion? = null
+    ): TextStyle {
+        val mergedSpanStyle: SpanStyle = spanStyle.fastMerge(
+            color = color,
+            brush = null,
+            alpha = Float.NaN,
+            fontSize = fontSize,
+            fontWeight = fontWeight,
+            fontStyle = fontStyle,
+            fontSynthesis = fontSynthesis,
+            fontFamily = fontFamily,
+            fontFeatureSettings = fontFeatureSettings,
+            letterSpacing = letterSpacing,
+            baselineShift = baselineShift,
+            textGeometricTransform = textGeometricTransform,
+            localeList = localeList,
+            background = background,
+            textDecoration = textDecoration,
+            shadow = shadow,
+            platformStyle = platformStyle?.spanStyle,
+            drawStyle = drawStyle
+        )
+        val mergedParagraphStyle: ParagraphStyle = paragraphStyle.fastMerge(
+            textAlign = textAlign,
+            textDirection = textDirection,
+            lineHeight = lineHeight,
+            textIndent = textIndent,
+            platformStyle = platformStyle?.paragraphStyle,
+            lineHeightStyle = lineHeightStyle,
+            lineBreak = lineBreak,
+            hyphens = hyphens,
+            textMotion = textMotion
+        )
+        if (spanStyle === mergedSpanStyle && paragraphStyle === mergedParagraphStyle) return this
+        return TextStyle(mergedSpanStyle, mergedParagraphStyle)
+    }
+
+    /**
      * Returns a new text style that is a combination of this style and the given [other] style.
      *
      * @see merge
@@ -560,7 +658,6 @@
             "copy constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     fun copy(
         color: Color = this.spanStyle.color,
         fontSize: TextUnit = this.spanStyle.fontSize,
@@ -625,7 +722,6 @@
             "copy constructor.",
         level = DeprecationLevel.HIDDEN
     )
-    @OptIn(ExperimentalTextApi::class)
     fun copy(
         color: Color = this.spanStyle.color,
         fontSize: TextUnit = this.spanStyle.fontSize,
@@ -686,7 +782,12 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
+    @Deprecated(
+        "TextStyle copy constructors that do not take new stable parameters " +
+            "like LineBreak, Hyphens, and TextMotion are deprecated. Please use the new stable " +
+            "copy constructor.",
+        level = DeprecationLevel.HIDDEN
+    )
     fun copy(
         color: Color = this.spanStyle.color,
         fontSize: TextUnit = this.spanStyle.fontSize,
@@ -749,7 +850,6 @@
         )
     }
 
-    @ExperimentalTextApi
     fun copy(
         color: Color = this.spanStyle.color,
         fontSize: TextUnit = this.spanStyle.fontSize,
@@ -814,7 +914,6 @@
         )
     }
 
-    @ExperimentalTextApi
     fun copy(
         brush: Brush?,
         alpha: Float = this.spanStyle.alpha,
@@ -880,9 +979,6 @@
     /**
      * The brush to use when drawing text. If not null, overrides [color].
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val brush: Brush? get() = this.spanStyle.brush
 
     /**
@@ -894,9 +990,6 @@
      * Opacity of text. This value is either provided along side Brush, or via alpha channel in
      * color.
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val alpha: Float get() = this.spanStyle.alpha
 
     /**
@@ -972,9 +1065,6 @@
     /**
      * Drawing style of text, whether fill in the text while drawing or stroke around the edges.
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val drawStyle: DrawStyle? get() = this.spanStyle.drawStyle
 
     /**
@@ -1022,9 +1112,6 @@
     /**
      * Text character placement configuration, whether to optimize for animated or static text.
      */
-    @ExperimentalTextApi
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalTextApi
     val textMotion: TextMotion? get() = this.paragraphStyle.textMotion
 
     override fun equals(other: Any?): Boolean {
@@ -1070,7 +1157,6 @@
         return result
     }
 
-    @OptIn(ExperimentalTextApi::class)
     override fun toString(): String {
         return "TextStyle(" +
             "color=$color, " +
diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt
index 7bbfe8d..83ba3cb 100644
--- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt
+++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt
@@ -58,7 +58,6 @@
     actual fun getBoundingBox(offset: Int): Rect
     actual fun getWordBoundary(offset: Int): TextRange
     actual fun paint(canvas: Canvas, color: Color, shadow: Shadow?, textDecoration: TextDecoration?)
-    @ExperimentalTextApi
     actual fun paint(
         canvas: Canvas,
         color: Color,
@@ -67,7 +66,6 @@
         drawStyle: DrawStyle?,
         blendMode: BlendMode
     )
-    @ExperimentalTextApi
     actual fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt
index 5ecc8b4..b91bd0f 100644
--- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt
+++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt
@@ -310,7 +310,6 @@
         para.paint(canvas.nativeCanvas, 0.0f, 0.0f)
     }
 
-    @ExperimentalTextApi
     override fun paint(
         canvas: Canvas,
         color: Color,
@@ -332,7 +331,6 @@
     }
 
     // TODO(b/229518449): Implement this paint function that draws text with a Brush.
-    @ExperimentalTextApi
     override fun paint(
         canvas: Canvas,
         brush: Brush,
diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt
index 67749c5..63c3537 100644
--- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt
+++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt
@@ -17,10 +17,8 @@
 package androidx.compose.ui.text.style
 
 import androidx.compose.runtime.Immutable
-import androidx.compose.ui.text.ExperimentalTextApi
 
 @Immutable
-@ExperimentalTextApi
 actual class TextMotion private constructor() {
     actual companion object {
         actual val Static: TextMotion = TextMotion()
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
index fa5f81c..3872a3b 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
@@ -44,7 +44,6 @@
 
 @RunWith(JUnit4::class)
 class SpanStyleTest {
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with default values`() {
         val style = SpanStyle()
@@ -62,7 +61,6 @@
         assertThat(style.drawStyle).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized brush`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -72,7 +70,6 @@
         assertThat(style.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized brush and alpha`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -83,7 +80,6 @@
         assertThat(style.alpha).isEqualTo(0.3f)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with gradient brush has unspecified color`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -93,7 +89,6 @@
         assertThat(style.color).isEqualTo(Color.Unspecified)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with SolidColor converts to regular color`() {
         val brush = SolidColor(Color.Red)
@@ -112,7 +107,6 @@
         assertThat(style.color).isEqualTo(color)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with half-transparent color`() {
         val color = Color.Red.copy(alpha = 0.5f)
@@ -204,7 +198,6 @@
         assertThat(style.fontFamily).isEqualTo(fontFamily)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized drawStyle`() {
         val stroke = Stroke(width = 4f)
@@ -461,7 +454,6 @@
         assertThat(mergedStyle.platformStyle).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with brush has other brush and no color`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -475,7 +467,6 @@
         assertThat(mergedStyle.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with unspecified brush has original brush`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -489,7 +480,6 @@
         assertThat(mergedStyle.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge brush with brush uses other's alpha`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -504,7 +494,6 @@
         assertThat(mergedStyle.alpha).isEqualTo(0.6f)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge brush with brush uses current alpha if other's is NaN`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -519,7 +508,6 @@
         assertThat(mergedStyle.alpha).isEqualTo(0.3f)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is null should use this' drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -530,7 +518,6 @@
         assertThat(newSpanStyle.drawStyle).isEqualTo(drawStyle1)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is set should use other's drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -890,7 +877,6 @@
         assertThat(lerpedStyle.platformStyle).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b specified and t is smaller than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -903,7 +889,6 @@
         assertThat(newStyle.color).isEqualTo(Color.Unspecified)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b specified and t is larger than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -916,7 +901,6 @@
         assertThat(newStyle.color).isEqualTo(Color.Red)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b not specified and t is larger than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
index a9c8a9f..d6376fd 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
@@ -75,7 +75,6 @@
         ).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_true_for_color_to_brush_change() {
         val style = TextStyle(color = Color.Red)
@@ -84,7 +83,6 @@
         ).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_true_for_brush_to_color_change() {
         val style = TextStyle(brush = SolidColor(Color.Green))
@@ -93,7 +91,6 @@
         ).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_true_for_brush_solid_color_change() {
         val style = TextStyle(brush = SolidColor(Color.Red))
@@ -103,7 +100,6 @@
         ).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_true_for_brush_shader_change() {
         val style = TextStyle(brush = Brush.linearGradient(listOf(Color.Black, Color.White)))
@@ -114,7 +110,6 @@
         ).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_true_for_brush_alpha_change() {
         val brush = Brush.linearGradient(listOf(Color.Black, Color.White))
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
index 5bff8e8..167bd68 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
@@ -49,7 +49,6 @@
     private val DefaultLineHeight = TextUnit.Unspecified
     private val DefaultColor = Color.Black
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun test_default_values() {
         // We explicitly expect the default values since we do not want to change these values.
@@ -79,7 +78,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun test_use_provided_values_brush() {
         val brush = Brush.linearGradient(listOf(Color.White, Color.Black))
@@ -102,7 +100,6 @@
         ).isEqualTo(Hyphens.Auto)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun test_use_provided_values_shader_brush_color_unspecified() {
         val brush = Brush.linearGradient(listOf(Color.White, Color.Black))
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
index c6cff1d..66378f5 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
@@ -18,10 +18,12 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Brush.Companion.linearGradient
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.DrawStyle
 import androidx.compose.ui.graphics.drawscope.Fill
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.lerp
@@ -35,8 +37,6 @@
 import androidx.compose.ui.text.style.Hyphens
 import androidx.compose.ui.text.style.LineBreak
 import androidx.compose.ui.text.style.LineHeightStyle
-import androidx.compose.ui.text.style.LineHeightStyle.Alignment
-import androidx.compose.ui.text.style.LineHeightStyle.Trim
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextDirection
@@ -50,13 +50,19 @@
 import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.sp
 import com.google.common.truth.Truth.assertThat
+import kotlin.reflect.KClass
+import kotlin.reflect.KParameter
+import kotlin.reflect.KProperty1
+import kotlin.reflect.KVisibility
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.jvm.jvmErasure
+import kotlin.test.assertNotEquals
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
 class TextStyleTest {
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with default values`() {
         val style = TextStyle()
@@ -85,7 +91,6 @@
         assertThat(style.lineBreak).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized brush`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -95,7 +100,6 @@
         assertThat(style.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized brush and alpha`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -106,7 +110,6 @@
         assertThat(style.alpha).isEqualTo(0.3f)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with gradient brush has unspecified color`() {
         val brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Red))
@@ -116,7 +119,6 @@
         assertThat(style.color).isEqualTo(Color.Unspecified)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with SolidColor converts to regular color`() {
         val brush = SolidColor(Color.Red)
@@ -126,7 +128,6 @@
         assertThat(style.color).isEqualTo(Color.Red)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized textMotion`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -134,7 +135,6 @@
         assertThat(style.textMotion).isEqualTo(TextMotion.Animated)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `empty copy with existing brush should not remove brush`() {
         val brush = Brush.linearGradient(listOf(Color.Red, Color.Blue))
@@ -151,7 +151,6 @@
         assertThat(style.copy().color).isEqualTo(Color.Red)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `empty copy with existing drawStyle should not remove drawStyle`() {
         val style = TextStyle(drawStyle = Stroke(2f))
@@ -160,7 +159,6 @@
     }
 
     @Suppress("DEPRECATION")
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `platformTextStyle copy with existing drawStyle should not remove drawStyle`() {
         val style = TextStyle(drawStyle = Stroke(2f))
@@ -170,7 +168,6 @@
         ).isEqualTo(Stroke(2f))
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `empty copy with existing hyphens should not remove hyphens`() {
         val style = TextStyle(hyphens = Hyphens.Auto)
@@ -185,7 +182,6 @@
         assertThat(style.copy(hyphens = Hyphens.None).hyphens).isEqualTo(Hyphens.None)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `brush copy with existing color should remove color`() {
         val style = TextStyle(color = Color.Red)
@@ -197,7 +193,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `color copy with existing brush should remove brush`() {
         val brush = Brush.linearGradient(listOf(Color.Red, Color.Blue))
@@ -209,7 +204,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `copy with textMotion returns new textMotion`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -246,7 +240,6 @@
         assertThat(style.color).isEqualTo(color)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with half-transparent color`() {
         val color = Color.Red.copy(alpha = 0.5f)
@@ -365,7 +358,6 @@
         assertThat(style.lineBreak).isEqualTo(LineBreak.Heading)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized drawStyle`() {
         val stroke = Stroke(width = 8f)
@@ -593,7 +585,6 @@
         assertThat(newStyle.textDecoration).isEqualTo(otherStyle.textDecoration)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is null should use this' drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -604,7 +595,6 @@
         assertThat(newTextStyle.drawStyle).isEqualTo(drawStyle1)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is set should use other's drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -758,7 +748,6 @@
         assertThat(mergedStyle.platformStyle).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with brush has other brush and no color`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -772,7 +761,6 @@
         assertThat(mergedStyle.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with unspecified brush has original brush`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -786,7 +774,6 @@
         assertThat(mergedStyle.brush).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge brush with brush uses other's alpha`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -801,7 +788,6 @@
         assertThat(mergedStyle.alpha).isEqualTo(0.6f)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge brush with brush uses current alpha if other's is NaN`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -856,7 +842,6 @@
         assertThat(mergedStyle.lineBreak).isEqualTo(otherStyle.lineBreak)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge null and non-null textMotion uses other's textMotion`() {
         val style = TextStyle(textMotion = null)
@@ -867,7 +852,6 @@
         assertThat(mergedStyle.textMotion).isEqualTo(otherStyle.textMotion)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge non-null and null textMotion uses original`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -878,7 +862,6 @@
         assertThat(mergedStyle.textMotion).isEqualTo(style.textMotion)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with both null textMotion uses null`() {
         val style = TextStyle(textMotion = null)
@@ -889,7 +872,6 @@
         assertThat(mergedStyle.textMotion).isEqualTo(null)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with both non-null textMotion uses other's textMotion`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -1290,7 +1272,6 @@
         assertThat(newStyle.textDecoration).isEqualTo(decoration2)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp drawStyle with a and b are not null and fraction is smaller than half`() {
         val drawStyle1 = Fill
@@ -1304,7 +1285,6 @@
         assertThat(newStyle.drawStyle).isEqualTo(drawStyle1)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp drawStyle with a and b are not null and fraction is larger than half`() {
         val drawStyle1 = Fill
@@ -1453,7 +1433,6 @@
         assertThat(newStyle.platformStyle).isEqualTo(style.platformStyle)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b specified and t is smaller than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -1466,7 +1445,6 @@
         assertThat(newStyle.color).isEqualTo(Color.Unspecified)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b specified and t is larger than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -1479,7 +1457,6 @@
         assertThat(newStyle.color).isEqualTo(Color.Red)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp brush with a specified, b not specified and t is larger than half`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -1532,7 +1509,6 @@
         assertThat(lerpedStyle.lineBreak).isSameInstanceAs(otherStyle.lineBreak)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp with non-null start, null end, closer to start has non-null textMotion`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -1543,7 +1519,6 @@
         assertThat(lerpedStyle.textMotion).isSameInstanceAs(style.textMotion)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp with non-null start, null end, closer to end has null textMotion`() {
         val style = TextStyle(textMotion = TextMotion.Animated)
@@ -1554,7 +1529,6 @@
         assertThat(lerpedStyle.textMotion).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp with null start, non-null end, closer to start has null textMotion`() {
         val style = TextStyle(textMotion = null)
@@ -1565,7 +1539,6 @@
         assertThat(lerpedStyle.textMotion).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp with null start, non-null end, closer to end has non-null textMotion`() {
         val style = TextStyle(textMotion = null)
@@ -1630,7 +1603,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `toSpanStyle return attributes with correct values for brush`() {
         val brush = Brush.linearGradient(listOf(Color.Blue, Color.Red))
@@ -1688,7 +1660,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `toParagraphStyle return attributes with correct values`() {
         val textAlign = TextAlign.Justify
@@ -1696,8 +1667,8 @@
         val lineHeight = 100.sp
         val textIndent = TextIndent(firstLine = 20.sp, restLine = 40.sp)
         val lineHeightStyle = LineHeightStyle(
-            alignment = Alignment.Center,
-            trim = Trim.None
+            alignment = LineHeightStyle.Alignment.Center,
+            trim = LineHeightStyle.Trim.None
         )
         val hyphens = Hyphens.Auto
         val lineBreak = LineBreak(
@@ -1815,14 +1786,14 @@
     fun `hashCode is different for different line height behavior`() {
         val style = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Bottom,
-                trim = Trim.None
+                alignment = LineHeightStyle.Alignment.Bottom,
+                trim = LineHeightStyle.Trim.None
             )
         )
         val otherStyle = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Center,
-                trim = Trim.Both
+                alignment = LineHeightStyle.Alignment.Center,
+                trim = LineHeightStyle.Trim.Both
             )
         )
 
@@ -1833,13 +1804,13 @@
     fun `copy with lineHeightStyle returns new lineHeightStyle`() {
         val style = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Bottom,
-                trim = Trim.None
+                alignment = LineHeightStyle.Alignment.Bottom,
+                trim = LineHeightStyle.Trim.None
             )
         )
         val newLineHeightStyle = LineHeightStyle(
-            alignment = Alignment.Center,
-            trim = Trim.Both
+            alignment = LineHeightStyle.Alignment.Center,
+            trim = LineHeightStyle.Trim.Both
         )
         val newStyle = style.copy(lineHeightStyle = newLineHeightStyle)
 
@@ -1850,8 +1821,8 @@
     fun `copy without lineHeightStyle uses existing lineHeightStyle`() {
         val style = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Bottom,
-                trim = Trim.None
+                alignment = LineHeightStyle.Alignment.Bottom,
+                trim = LineHeightStyle.Trim.None
             )
         )
         val newStyle = style.copy()
@@ -1893,14 +1864,14 @@
     fun `merge with both non-null lineHeightStyle returns other's lineHeightStyle`() {
         val style = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Center,
-                trim = Trim.None
+                alignment = LineHeightStyle.Alignment.Center,
+                trim = LineHeightStyle.Trim.None
             )
         )
         val otherStyle = TextStyle(
             lineHeightStyle = LineHeightStyle(
-                alignment = Alignment.Bottom,
-                trim = Trim.Both
+                alignment = LineHeightStyle.Alignment.Bottom,
+                trim = LineHeightStyle.Trim.Both
             )
         )
 
@@ -2017,4 +1988,124 @@
             )
         ).isEqualTo(TextDirection.Rtl)
     }
+
+    @Test
+    fun textStyle_allParamsMerge_nonDefaultWins() {
+        val subject = TextStyle.Default
+        val parameters = TextStyle::class.allConstructorParams()
+        val others = parameters.map { (param, constructor) ->
+            val args = mapOf(param to subject.getNotEqualValueFor(param))
+                .addPairwiseArgsToFix(parameters.map { it.first })
+            param to constructor.callBy(args)
+        }
+        for ((param, other) in others) {
+            val merged = subject.merge(other)
+            val inverseMerged = other.merge(subject)
+
+            // hand-rolling error messages since this is a all-params test
+            assertNotEquals(subject, merged,
+                "subject.merge(other) on param=`${param.name}` failed" +
+                    "\n\tmerged  = $merged" +
+                    "\n\tother   = $other" +
+                    "\n\tsubject = $subject"
+            )
+            assertNotEquals(subject, inverseMerged,
+                "other.merge(subject) on param=`${param.name}` failed" +
+                    "\n\tmerged  = $merged" +
+                    "\n\tother   = $other" +
+                    "\n\tsubject = $subject"
+            )
+        }
+    }
+}
+
+/**
+ * Some params such as (brush, alpha) require a sibling parameter.
+ *
+ * This function is to fix the args map for them.
+ */
+private fun Map<KParameter, Any?>.addPairwiseArgsToFix(
+    params: Collection<KParameter>
+): Map<KParameter, Any?> {
+    val name = keys.first().name
+    return this + when (name) {
+        "brush" -> {
+            val alpha = params.first { it.name == "alpha" }
+            mapOf(alpha to 0.7f)
+        }
+        "alpha" -> {
+            val brush = params.first { it.name == "brush" }
+            mapOf(brush to linearGradient(listOf(Color.Red)))
+        }
+        else -> emptyMap()
+    }
+}
+
+/**
+ * All parameters on any public constructor, distinct by name
+ */
+private fun KClass<TextStyle>.allConstructorParams() = this.constructors
+    .filter { it.visibility == KVisibility.PUBLIC }
+    .flatMap { it.parameters.map { param -> param to it } }
+    .fastDistinctBy { it.first.name }
+
+/**
+ * Compute a distinct value for [KParameter] from the value in [kParameter]
+ */
+@OptIn(ExperimentalTextApi::class)
+private fun TextStyle.getNotEqualValueFor(kParameter: KParameter): Any {
+    val prop: KProperty1<TextStyle, *> =
+        TextStyle::class.memberProperties.first { it.name == kParameter.name }
+    val currentValue = prop.get(this)
+    val newValue: Any = when (kParameter.type.jvmErasure) {
+        Color::class -> Color.Magenta
+        TextUnit::class -> 4.em
+        FontWeight::class -> FontWeight(712)
+        FontStyle::class -> FontStyle.Italic
+        FontSynthesis::class -> FontSynthesis.Weight
+        FontFamily::class -> FontFamily.Cursive
+        String::class -> (currentValue as? String ?: "") + " more text"
+        BaselineShift::class -> BaselineShift.Superscript
+        TextGeometricTransform::class -> TextGeometricTransform(0.5f, 0.5f)
+        LocaleList::class -> LocaleList("fr")
+        TextDecoration::class -> TextDecoration.Underline
+        Shadow::class -> Shadow(color = Color.Red)
+        TextAlign::class -> TextAlign.Justify
+        TextDirection::class -> TextDirection.Rtl
+        TextIndent::class -> TextIndent(10.sp)
+        PlatformTextStyle::class -> PlatformTextStyle(emojiSupportMatch = EmojiSupportMatch.None)
+        LineHeightStyle::class -> LineHeightStyle(
+            LineHeightStyle.Alignment.Center,
+            LineHeightStyle.Trim.None
+        )
+        LineBreak::class -> LineBreak.Heading
+        Hyphens::class -> Hyphens.Auto
+        DrawStyle::class -> Stroke(1f)
+        TextMotion::class -> TextMotion.Animated
+        Brush::class -> linearGradient(listOf(Color.Blue, Color.Red))
+        Float::class -> (currentValue as? Float).nextDistinct()
+        Int::class -> (currentValue as? Int ?: 0) + 4
+        else -> TODO("Please add an branch to this switch for ${kParameter.type}")
+    }
+    require(newValue != currentValue) {
+        "Logic for making distinct values failed, update this function so that" +
+            " $currentValue != $newValue for ${prop.name}; you may need to add logic" +
+            " based on $currentValue inside the switch"
+    }
+    return newValue
+}
+
+/**
+ * Floats can break addition on NaN and Infinity, hardcode distinct values
+ */
+private fun Float?.nextDistinct(): Float {
+    return if (this == null) {
+        1f
+    } else if (this.isNaN()) {
+        2f
+    } else if (this.isInfinite()) {
+        3f
+    } else {
+        this + 4.2f
+    }
 }
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 48fd02b..b03200b 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -144,14 +144,15 @@
     ctor public Modifier.Node();
     method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
     method public final androidx.compose.ui.Modifier.Node getNode();
+    method public boolean getShouldAutoInvalidate();
     method public final boolean isAttached();
     method public void onAttach();
     method public void onDetach();
     method public void onReset();
-    method public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     property public final kotlinx.coroutines.CoroutineScope coroutineScope;
     property public final boolean isAttached;
     property public final androidx.compose.ui.Modifier.Node node;
+    property public boolean shouldAutoInvalidate;
   }
 
   @androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -1480,6 +1481,10 @@
     method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
   }
 
+  public final class NestedScrollNodeKt {
+    method public static androidx.compose.ui.node.DelegatableNode nestedScrollModifierNode(androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
   @kotlin.jvm.JvmInline public final value class NestedScrollSource {
     field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
   }
@@ -2224,7 +2229,7 @@
   }
 
   public interface ModifierLocalNode extends androidx.compose.ui.modifier.ModifierLocalReadScope androidx.compose.ui.node.DelegatableNode {
-    method public default <T> T! getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
+    method public default <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
     method public default androidx.compose.ui.modifier.ModifierLocalMap getProvidedValues();
     method public default <T> void provide(androidx.compose.ui.modifier.ModifierLocal<T> key, T value);
     property public default androidx.compose.ui.modifier.ModifierLocalMap providedValues;
@@ -2277,7 +2282,8 @@
 
   public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
     ctor public DelegatingNode();
-    method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+    method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+    method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
   }
 
   public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2309,7 +2315,7 @@
 
   public final class LayoutModifierNodeKt {
     method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
-    method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+    method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
     method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
   }
 
@@ -2317,14 +2323,12 @@
     ctor public ModifierNodeElement();
     method public abstract N create();
     method public abstract boolean equals(Object? other);
-    method public boolean getAutoInvalidate();
     method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
     method public final String? getNameFallback();
     method public final Object? getValueOverride();
     method public abstract int hashCode();
     method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
-    method public abstract N update(N node);
-    property public boolean autoInvalidate;
+    method public abstract void update(N node);
     property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
     property public final String? nameFallback;
     property public final Object? valueOverride;
@@ -2378,7 +2382,6 @@
   }
 
   public final class SemanticsModifierNodeKt {
-    method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
     method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
   }
 
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 95fddb6..2520afc 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -154,14 +154,16 @@
     ctor public Modifier.Node();
     method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
     method public final androidx.compose.ui.Modifier.Node getNode();
+    method public boolean getShouldAutoInvalidate();
     method public final boolean isAttached();
     method public void onAttach();
     method public void onDetach();
     method public void onReset();
-    method public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.ui.ExperimentalComposeUiApi public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     property public final kotlinx.coroutines.CoroutineScope coroutineScope;
     property public final boolean isAttached;
     property public final androidx.compose.ui.Modifier.Node node;
+    property public boolean shouldAutoInvalidate;
   }
 
   @androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -1609,6 +1611,10 @@
     method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
   }
 
+  public final class NestedScrollNodeKt {
+    method public static androidx.compose.ui.node.DelegatableNode nestedScrollModifierNode(androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
   @kotlin.jvm.JvmInline public final value class NestedScrollSource {
     field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
   }
@@ -2434,7 +2440,7 @@
   }
 
   public interface ModifierLocalNode extends androidx.compose.ui.modifier.ModifierLocalReadScope androidx.compose.ui.node.DelegatableNode {
-    method public default <T> T! getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
+    method public default <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
     method public default androidx.compose.ui.modifier.ModifierLocalMap getProvidedValues();
     method public default <T> void provide(androidx.compose.ui.modifier.ModifierLocal<T> key, T value);
     property public default androidx.compose.ui.modifier.ModifierLocalMap providedValues;
@@ -2491,7 +2497,8 @@
 
   public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
     ctor public DelegatingNode();
-    method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+    method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+    method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
   }
 
   public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2530,7 +2537,7 @@
 
   public final class LayoutModifierNodeKt {
     method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
-    method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+    method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
     method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
   }
 
@@ -2538,14 +2545,12 @@
     ctor public ModifierNodeElement();
     method public abstract N create();
     method public abstract boolean equals(Object? other);
-    method public boolean getAutoInvalidate();
     method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
     method public final String? getNameFallback();
     method public final Object? getValueOverride();
     method public abstract int hashCode();
     method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
-    method public abstract N update(N node);
-    property public boolean autoInvalidate;
+    method public abstract void update(N node);
     property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
     property public final String? nameFallback;
     property public final Object? valueOverride;
@@ -2600,7 +2605,6 @@
   }
 
   public final class SemanticsModifierNodeKt {
-    method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
     method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
   }
 
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index fe0c61d..b449e04 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -144,14 +144,15 @@
     ctor public Modifier.Node();
     method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
     method public final androidx.compose.ui.Modifier.Node getNode();
+    method public boolean getShouldAutoInvalidate();
     method public final boolean isAttached();
     method public void onAttach();
     method public void onDetach();
     method public void onReset();
-    method public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     property public final kotlinx.coroutines.CoroutineScope coroutineScope;
     property public final boolean isAttached;
     property public final androidx.compose.ui.Modifier.Node node;
+    property public boolean shouldAutoInvalidate;
   }
 
   @androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -1480,6 +1481,10 @@
     method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
   }
 
+  public final class NestedScrollNodeKt {
+    method public static androidx.compose.ui.node.DelegatableNode nestedScrollModifierNode(androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
   @kotlin.jvm.JvmInline public final value class NestedScrollSource {
     field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
   }
@@ -2231,7 +2236,7 @@
   }
 
   public interface ModifierLocalNode extends androidx.compose.ui.modifier.ModifierLocalReadScope androidx.compose.ui.node.DelegatableNode {
-    method public default <T> T! getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
+    method public default <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
     method public default androidx.compose.ui.modifier.ModifierLocalMap getProvidedValues();
     method public default <T> void provide(androidx.compose.ui.modifier.ModifierLocal<T> key, T value);
     property public default androidx.compose.ui.modifier.ModifierLocalMap providedValues;
@@ -2325,7 +2330,8 @@
 
   public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
     ctor public DelegatingNode();
-    method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+    method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+    method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
   }
 
   public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2357,7 +2363,7 @@
 
   public final class LayoutModifierNodeKt {
     method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
-    method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+    method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
     method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
   }
 
@@ -2365,14 +2371,12 @@
     ctor public ModifierNodeElement();
     method public abstract N create();
     method public abstract boolean equals(Object? other);
-    method public boolean getAutoInvalidate();
     method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
     method public final String? getNameFallback();
     method public final Object? getValueOverride();
     method public abstract int hashCode();
     method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
-    method public abstract N update(N node);
-    property public boolean autoInvalidate;
+    method public abstract void update(N node);
     property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
     property public final String? nameFallback;
     property public final Object? valueOverride;
@@ -2426,7 +2430,6 @@
   }
 
   public final class SemanticsModifierNodeKt {
-    method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
     method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
   }
 
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
index 31c7afa..4335d1e 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
@@ -143,9 +143,8 @@
     }
     data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
         override fun create() = CircleNode(color)
-        override fun update(node: CircleNode): CircleNode {
+        override fun update(node: CircleNode) {
             node.color = color
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "color"
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayoutSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayoutSample.kt
index 7ef664a..6c41b9f8 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayoutSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayoutSample.kt
@@ -212,9 +212,8 @@
         val padding: Dp
     ) : ModifierNodeElement<VerticalPadding>() {
         override fun create() = VerticalPadding(padding)
-        override fun update(node: VerticalPadding): VerticalPadding {
+        override fun update(node: VerticalPadding) {
             node.padding = padding
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "verticalPadding"
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierCompositionLocalSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierCompositionLocalSample.kt
index df7fd7a..05eb8f0 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierCompositionLocalSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierCompositionLocalSample.kt
@@ -44,7 +44,7 @@
     }
     val BackgroundColorModifierElement = object : ModifierNodeElement<BackgroundColor>() {
         override fun create() = BackgroundColor()
-        override fun update(node: BackgroundColor) = node
+        override fun update(node: BackgroundColor) {}
         override fun hashCode() = System.identityHashCode(this)
         override fun equals(other: Any?) = (other === this)
         override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
index 6cc20cfc1..1039565 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
@@ -49,11 +49,13 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.node.requireLayoutDirection
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.heading
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.launch
 
@@ -119,7 +121,7 @@
 @ExperimentalComposeUiApi
 @Sampled
 @Composable
-fun DelegatedNodeSample() {
+fun DelegatedNodeSampleExplicit() {
     class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
         override fun onPointerEvent(
             pointerEvent: PointerEvent,
@@ -139,7 +141,7 @@
             get() = gesture.onTap
             set(value) { gesture.onTap = value }
 
-        val gesture = delegated { TapGestureNode(onTap) }
+        val gesture = delegate(TapGestureNode(onTap))
 
         override fun onPointerEvent(
             pointerEvent: PointerEvent,
@@ -166,6 +168,151 @@
 @ExperimentalComposeUiApi
 @Sampled
 @Composable
+fun DelegatedNodeSampleImplicit() {
+    class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
+        override fun onPointerEvent(
+            pointerEvent: PointerEvent,
+            pass: PointerEventPass,
+            bounds: IntSize
+        ) {
+            // ...
+        }
+
+        override fun onCancelPointerInput() {
+            // ...
+        }
+    }
+
+    class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
+        override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
+            .apply {
+                onClick {
+                    onTap()
+                    true
+                }
+            }
+    }
+    class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
+        var onTap: () -> Unit
+            get() = gesture.onTap
+            set(value) {
+                gesture.onTap = value
+                semantics.onTap = value
+            }
+
+        val gesture = delegate(TapGestureNode(onTap))
+        val semantics = delegate(TapSemanticsNode(onTap))
+    }
+}
+
+@ExperimentalComposeUiApi
+@Sampled
+@Composable
+fun LazyDelegationExample() {
+    class ExpensivePositionHandlingOnPointerEvents() : PointerInputModifierNode, DelegatingNode() {
+
+        val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+            override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                // ...
+            }
+        }
+
+        override fun onPointerEvent(
+            pointerEvent: PointerEvent,
+            pass: PointerEventPass,
+            bounds: IntSize
+        ) {
+            // wait until first pointer event to start listening to global
+            // position
+            if (!globalAwareNode.isAttached) {
+                delegate(globalAwareNode)
+            }
+            // normal input processing
+        }
+
+        override fun onCancelPointerInput() {
+            // ...
+        }
+    }
+
+    class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
+        override fun onPointerEvent(
+            pointerEvent: PointerEvent,
+            pass: PointerEventPass,
+            bounds: IntSize
+        ) {
+            // ...
+        }
+
+        override fun onCancelPointerInput() {
+            // ...
+        }
+    }
+
+    class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
+        override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
+            .apply {
+                onClick {
+                    onTap()
+                    true
+                }
+            }
+    }
+    class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
+        var onTap: () -> Unit
+            get() = gesture.onTap
+            set(value) {
+                gesture.onTap = value
+                semantics.onTap = value
+            }
+
+        val gesture = delegate(TapGestureNode(onTap))
+        val semantics = delegate(TapSemanticsNode(onTap))
+    }
+}
+
+@Sampled
+fun ConditionalDelegationExample() {
+    class MyModifierNode(global: Boolean) : DelegatingNode() {
+        val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+            override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                // ...
+            }
+        }.also {
+            if (global) delegate(it)
+        }
+        var global: Boolean = global
+            set(value) {
+                if (global && !value) {
+                    undelegate(globalAwareNode)
+                } else if (!global && value) {
+                    delegate(globalAwareNode)
+                }
+                field = value
+            }
+    }
+}
+
+@Sampled
+fun DelegateInAttachSample() {
+    class MyModifierNode : DelegatingNode() {
+        val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+            override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                // ...
+            }
+        }
+        override fun onAttach() {
+            // one can conditionally delegate in attach, for instance if certain conditions are met
+            if (requireLayoutDirection() == LayoutDirection.Rtl) {
+                delegate(globalAwareNode)
+            }
+        }
+    }
+}
+
+@ExperimentalComposeUiApi
+@Sampled
+@Composable
 fun ModifierNodeElementSample() {
     class Circle(var color: Color) : DrawModifierNode, Modifier.Node() {
         override fun ContentDrawScope.draw() {
@@ -176,9 +323,8 @@
         val color: Color
     ) : ModifierNodeElement<Circle>() {
         override fun create() = Circle(color)
-        override fun update(node: Circle): Circle {
+        override fun update(node: Circle) {
             node.color = color
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "circle"
@@ -203,9 +349,8 @@
     val HeadingElement = object : ModifierNodeElement<HeadingNode>() {
         override fun create() = HeadingNode()
 
-        override fun update(node: HeadingNode): HeadingNode {
+        override fun update(node: HeadingNode) {
             // Nothing to update.
-            return node
         }
 
         override fun InspectorInfo.inspectableProperties() {
@@ -244,9 +389,8 @@
         val callback: (PointerEvent) -> Unit
     ) : ModifierNodeElement<OnPointerEventNode>() {
         override fun create() = OnPointerEventNode(callback)
-        override fun update(node: OnPointerEventNode): OnPointerEventNode {
+        override fun update(node: OnPointerEventNode) {
             node.callback = callback
-            return node
         }
 
         override fun InspectorInfo.inspectableProperties() {
@@ -271,9 +415,8 @@
 
     data class LogSizeElement(val id: String) : ModifierNodeElement<SizeLoggerNode>() {
         override fun create(): SizeLoggerNode = SizeLoggerNode(id)
-        override fun update(node: SizeLoggerNode): SizeLoggerNode {
+        override fun update(node: SizeLoggerNode) {
             node.id = id
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "logSize"
@@ -305,9 +448,8 @@
 
     data class PositionLoggerElement(val id: String) : ModifierNodeElement<PositionLoggerNode>() {
         override fun create() = PositionLoggerNode(id)
-        override fun update(node: PositionLoggerNode): PositionLoggerNode {
+        override fun update(node: PositionLoggerNode) {
             node.id = id
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "logPosition"
@@ -334,9 +476,8 @@
         val logger: Logger
     ) : ModifierNodeElement<ProvideLoggerNode>() {
         override fun create() = ProvideLoggerNode(logger)
-        override fun update(node: ProvideLoggerNode): ProvideLoggerNode {
+        override fun update(node: ProvideLoggerNode) {
             node.provide(loggerLocal, logger)
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "provideLogger"
@@ -356,9 +497,8 @@
         val id: String
     ) : ModifierNodeElement<SizeLoggerNode>() {
         override fun create() = SizeLoggerNode(id)
-        override fun update(node: SizeLoggerNode): SizeLoggerNode {
+        override fun update(node: SizeLoggerNode) {
             node.id = id
-            return node
         }
         override fun InspectorInfo.inspectableProperties() {
             name = "logSize"
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 27e78de..df76fb54 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -286,8 +286,6 @@
         val switchRoleNode = toggleableNode.replacedChildren.last()
         val switchRoleNodeInfo = provider.createAccessibilityNodeInfo(switchRoleNode.id)!!
         assertEquals("android.view.View", switchRoleNodeInfo.className)
-// TODO(aelias)
-        // assertEquals("Switch", switchRoleNodeInfo.roleDescription)
 
         val stateDescription = when {
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
@@ -3771,8 +3769,8 @@
         assertNotNull("Button has no children", lastChild)
         assertTrue("Last child should be fake Button role node", lastChild!!.isFake)
         assertEquals(
+            Role.Button,
             lastChild.unmergedConfig.getOrNull(SemanticsProperties.Role),
-            Role.Button
         )
     }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index ece6982..b4cc4f9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -31,9 +31,7 @@
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.toAndroidRect
-import androidx.compose.ui.node.InnerNodeCoordinator
 import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.SemanticsModifierNode
 import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat
 import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat.SemanticsNodeCopy
@@ -46,7 +44,6 @@
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.ScrollAxisRange
-import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
@@ -1604,19 +1601,10 @@
         properties: (SemanticsPropertyReceiver.() -> Unit)
     ): SemanticsNode {
         val layoutNode = LayoutNode(semanticsId = id)
-        val nodeCoordinator = InnerNodeCoordinator(layoutNode)
-        val modifierNode = object : SemanticsModifierNode, Modifier.Node() {
-            override val semanticsConfiguration = SemanticsConfiguration().also {
-                it.isMergingSemanticsOfDescendants = mergeDescendants
-                it.properties()
-            }
+        layoutNode.modifier = Modifier.semantics(mergeDescendants) {
+            properties()
         }
-        modifierNode.updateCoordinator(nodeCoordinator)
-        return SemanticsNode(
-            modifierNode,
-            true,
-            layoutNode
-        )
+        return SemanticsNode(layoutNode, true)
     }
 
     private fun createSemanticsNodeWithChildren(
@@ -1626,20 +1614,10 @@
     ): SemanticsNode {
         val layoutNode = LayoutNode(semanticsId = id)
         layoutNode.zSortedChildren.addAll(children.map { it.layoutNode })
-        val nodeCoordinator = InnerNodeCoordinator(layoutNode)
-        val modifierNode = object : SemanticsModifierNode, Modifier.Node() {
-            override val semanticsConfiguration = SemanticsConfiguration().also {
-                it.properties()
-            }
-        }
-        modifierNode.updateCoordinator(nodeCoordinator)
-
-        val semanticsNode = SemanticsNode(modifierNode, true, layoutNode)
         layoutNode.modifier = Modifier.semantics {
             properties()
         }
-
-        return semanticsNode
+        return SemanticsNode(layoutNode, true)
     }
 
     private fun createSemanticsNodeWithAdjustedBoundsWithProperties(
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
index 47f9c79..adf79b7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
@@ -20,15 +20,18 @@
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.LayoutIdModifier
 import androidx.compose.ui.layout.LayoutIdParentData
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.ParentDataModifierNode
 import androidx.compose.ui.node.Ref
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
@@ -161,6 +164,27 @@
     }
 
     @Test
+    fun delegatedParentData() {
+        val node = object : DelegatingNode() {
+            val pd = delegate(LayoutIdModifier("data"))
+        }
+        runOnUiThread {
+            activity.setContent {
+                Layout({
+                    Layout(
+                        modifier = Modifier.elementFor(node),
+                        content = {}
+                    ) { _, _ -> layout(0, 0) {} }
+                }) { measurables, constraints ->
+                    val placeable = measurables[0].measure(constraints)
+                    assertEquals("data", (placeable.parentData as? LayoutIdParentData)?.layoutId)
+                    layout(0, 0) { }
+                }
+            }
+        }
+    }
+
+    @Test
     fun implementingBothParentDataAndLayoutModifier() {
         val parentData = "data"
         runOnUiThread {
@@ -203,7 +227,9 @@
 private data class ParentDataAndLayoutElement(val data: String) :
     ModifierNodeElement<ParentDataAndLayoutNode>() {
     override fun create() = ParentDataAndLayoutNode(data)
-    override fun update(node: ParentDataAndLayoutNode) = node.also { it.data = data }
+    override fun update(node: ParentDataAndLayoutNode) {
+        node.data = data
+    }
 }
 
 class ParentDataAndLayoutNode(var data: String) : Modifier.Node(), LayoutModifierNode,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
index 8bed753..0a41454 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
@@ -44,14 +44,19 @@
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.LayoutModifierImpl
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -721,6 +726,100 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testDelegatedDrawNodesDraw() {
+        val testTag = "testTag"
+        val size = 200
+
+        val node = object : DelegatingNode() {
+            val draw = delegate(DrawBackgroundModifier {
+                drawRect(Color.Red)
+            })
+        }
+
+        rule.setContent {
+            AtLeastSize(
+                size = size,
+                modifier = Modifier
+                    .testTag(testTag)
+                    .elementFor(node)
+            ) { }
+        }
+
+        rule.onNodeWithTag(testTag).apply {
+            captureToBitmap().apply {
+                assertEquals(Color.Red.toArgb(), getPixel(1, 1))
+            }
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testMultipleDelegatedDrawNodes() {
+        val testTag = "testTag"
+
+        val node = object : DelegatingNode() {
+            val a = delegate(DrawBackgroundModifier {
+                drawRect(
+                    Color.Red,
+                    size = Size(10f, 10f)
+                )
+            })
+
+            val b = delegate(DrawBackgroundModifier {
+                drawRect(
+                    Color.Blue,
+                    topLeft = Offset(10f, 0f),
+                    size = Size(10f, 10f))
+            })
+        }
+
+        rule.setContent {
+            AtLeastSize(
+                size = 200,
+                modifier = Modifier
+                    .testTag(testTag)
+                    .elementFor(node)
+            ) { }
+        }
+
+        rule.onNodeWithTag(testTag).apply {
+            captureToBitmap().apply {
+                assertEquals(Color.Red.toArgb(), getPixel(1, 1))
+                assertEquals(Color.Blue.toArgb(), getPixel(11, 1))
+            }
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testDelegatedLayoutModifierNode() {
+        val testTag = "testTag"
+
+        val node = object : DelegatingNode() {
+            val a = delegate(LayoutModifierImpl { measurable, constraints ->
+                val p = measurable.measure(constraints)
+                layout(10.dp.roundToPx(), 10.dp.roundToPx()) {
+                    p.place(0, 0)
+                }
+            })
+        }
+
+        rule.setContent {
+            Box(
+                modifier = Modifier
+                    .testTag(testTag)
+                    .elementFor(node)
+            )
+        }
+
+        rule
+            .onNodeWithTag(testTag)
+            .assertWidthIsEqualTo(10.dp)
+            .assertHeightIsEqualTo(10.dp)
+    }
+
     // captureToImage() requires API level 26
     @RequiresApi(Build.VERSION_CODES.O)
     private fun SemanticsNodeInteraction.captureToBitmap() = captureToImage().asAndroidBitmap()
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
index bb14507..9e53f72 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
@@ -20,7 +20,6 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.ModifierNodeElement
@@ -32,7 +31,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
-@OptIn(ExperimentalComposeUiApi::class)
 @MediumTest
 @RunWith(Parameterized::class)
 class CombinedFocusModifierNodeTest(private val delegatedFocusTarget: Boolean) {
@@ -202,8 +200,8 @@
         val combinedFocusNode: CombinedFocusNode
     ) : ModifierNodeElement<CombinedFocusNode>() {
         override fun create(): CombinedFocusNode = combinedFocusNode
-        override fun update(node: CombinedFocusNode) = node.apply {
-            focusState = combinedFocusNode.focusState
+        override fun update(node: CombinedFocusNode) {
+            node.focusState = combinedFocusNode.focusState
         }
     }
 
@@ -213,15 +211,10 @@
         fun initParameters() =
             listOf(
                 false,
-                // TODO: Delegation does not work right now because a delegated node can
-                //  reference the node delegating to it, but it can't reference a delegated node in
-                //  its parent. For some use-cases, a parent needs to invalidate a child. We cannot
-                //  do this when the child is a delegated node.
-                // true
+                true
             )
     }
 
-    @OptIn(ExperimentalComposeUiApi::class)
     private class CombinedFocusNode(delegatedFocusTarget: Boolean) :
         FocusRequesterModifierNode,
         FocusEventModifierNode,
@@ -229,7 +222,7 @@
         DelegatingNode() {
 
         init {
-            if (delegatedFocusTarget) delegated { FocusTargetModifierNode() }
+            if (delegatedFocusTarget) delegate(FocusTargetModifierNode())
         }
 
         lateinit var focusState: FocusState
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
index 21d476d..f17a1a2 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
@@ -39,6 +39,10 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import android.view.KeyEvent as AndroidKeyEvent
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.semantics.elementFor
+import org.junit.Ignore
+import org.mockito.kotlin.inOrder
 
 /**
  * This test verifies that an Android key event triggers a Compose key event. More detailed test
@@ -98,4 +102,143 @@
             assertThat(keyConsumed).isTrue()
         }
     }
+
+    @Ignore("b/279178695")
+    @Test
+    fun delegated_onKeyEvent_triggered() {
+        // Arrange.
+        lateinit var ownerView: View
+        var receivedKeyEvent: KeyEvent? = null
+        val focusRequester = FocusRequester()
+        val node = object : DelegatingNode() {
+            val ki = delegate(object : KeyInputModifierNode, Modifier.Node() {
+                override fun onKeyEvent(event: KeyEvent): Boolean {
+                    receivedKeyEvent = event
+                    return true
+                }
+
+                override fun onPreKeyEvent(event: KeyEvent): Boolean {
+                    return false
+                }
+            })
+        }
+
+        rule.setFocusableContent {
+            ownerView = LocalView.current
+            Box(
+                modifier = Modifier
+                    .focusRequester(focusRequester)
+                    .focusTarget()
+                    .elementFor(node)
+            )
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+        }
+
+        // Act.
+        val keyConsumed = rule.runOnIdle {
+            ownerView.dispatchKeyEvent(AndroidKeyEvent(keyEventAction, KeyCodeA))
+        }
+
+        rule.waitUntil { receivedKeyEvent != null }
+
+        // Assert.
+        rule.runOnIdle {
+            val keyEvent = checkNotNull(receivedKeyEvent)
+            assertThat(keyEvent.type).isEqualTo(
+                when (keyEventAction) {
+                    ActionUp -> KeyUp
+                    ActionDown -> KeyDown
+                    else -> error("No tests for this key action.")
+                }
+            )
+            assertThat(keyEvent.key).isEqualTo(A)
+            assertThat(keyConsumed).isTrue()
+        }
+    }
+
+    @Ignore("b/279178695")
+    @Test
+    fun delegated_multiple_onKeyEvent_triggered() {
+        // Arrange.
+        lateinit var ownerView: View
+        var receivedKeyEvent1: KeyEvent? = null
+        var receivedKeyEvent2: KeyEvent? = null
+        val eventLog = mutableListOf<KeyEvent>()
+        val focusRequester = FocusRequester()
+        val node = object : DelegatingNode() {
+            val a = delegate(object : KeyInputModifierNode, Modifier.Node() {
+                override fun onKeyEvent(event: KeyEvent): Boolean {
+                    receivedKeyEvent1 = event
+                    eventLog.add(event)
+                    return false
+                }
+
+                override fun onPreKeyEvent(event: KeyEvent): Boolean {
+                    return false
+                }
+            })
+            val b = delegate(object : KeyInputModifierNode, Modifier.Node() {
+                override fun onKeyEvent(event: KeyEvent): Boolean {
+                    receivedKeyEvent2 = event
+                    eventLog.add(event)
+                    return false
+                }
+
+                override fun onPreKeyEvent(event: KeyEvent): Boolean {
+                    return false
+                }
+            })
+        }
+
+        rule.setFocusableContent {
+            ownerView = LocalView.current
+            Box(
+                modifier = Modifier
+                    .focusRequester(focusRequester)
+                    .elementFor(node)
+                    .focusTarget()
+            )
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+        }
+
+        // Act.
+        val keyConsumed = rule.runOnIdle {
+            ownerView.dispatchKeyEvent(AndroidKeyEvent(keyEventAction, KeyCodeA))
+        }
+
+        rule.waitUntil { receivedKeyEvent2 != null }
+
+        // Assert.
+        rule.runOnIdle {
+            val keyEvent1 = checkNotNull(receivedKeyEvent1)
+            assertThat(keyEvent1.type).isEqualTo(
+                when (keyEventAction) {
+                    ActionUp -> KeyUp
+                    ActionDown -> KeyDown
+                    else -> error("No tests for this key action.")
+                }
+            )
+            assertThat(keyEvent1.key).isEqualTo(A)
+            assertThat(keyConsumed).isFalse()
+
+            val keyEvent2 = checkNotNull(receivedKeyEvent2)
+            assertThat(keyEvent2.type).isEqualTo(
+                when (keyEventAction) {
+                    ActionUp -> KeyUp
+                    ActionDown -> KeyDown
+                    else -> error("No tests for this key action.")
+                }
+            )
+            assertThat(keyEvent2.key).isEqualTo(A)
+            assertThat(keyConsumed).isFalse()
+
+            assertThat(eventLog)
+                .containsExactly(receivedKeyEvent1, receivedKeyEvent2)
+                .inOrder()
+        }
+    }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
index a13aa46..4bc3f57 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
@@ -3529,7 +3529,7 @@
     }
 
     private fun areEqual(actualNode: Node, expectedNode: Node): Boolean {
-        if (actualNode.pointerInputNode !== expectedNode.pointerInputNode) {
+        if (actualNode.modifierNode !== expectedNode.modifierNode) {
             return false
         }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
index 960fa84..4f8d561 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
@@ -17,17 +17,23 @@
 package androidx.compose.ui.input.pointer
 
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.IntSize
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -692,6 +698,79 @@
             assertThat(resultOfTimeoutOrNull).isNull()
         }
     }
+
+    @Test
+    @MediumTest
+    fun testDelegatedPointerEvent() {
+        val latch = CountDownLatch(1)
+        val emitter = PointerInputChangeEmitter()
+        val expectedChange = emitter.nextChange(Offset(5f, 5f))
+
+        var returnedChange: PointerEvent? = null
+
+        // Used to manually trigger a PointerEvent created from our PointerInputChange.
+        val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
+            awaitPointerEventScope {
+                returnedChange = awaitPointerEvent()
+                latch.countDown()
+            }
+        }
+        val node = object : DelegatingNode() {
+            val pointer = delegate(suspendingPointerInputModifierNode)
+        }
+
+        rule.setContent {
+            Box(Modifier.elementFor(node))
+        }
+
+        rule.runOnIdle {
+            suspendingPointerInputModifierNode.onPointerEvent(
+                expectedChange.toPointerEvent(),
+                PointerEventPass.Main,
+                IntSize(10, 10)
+            )
+        }
+
+        rule.runOnIdle {
+            assertTrue("Waiting for relaunch timed out", latch.await(200, TimeUnit.MILLISECONDS))
+            assertEquals(expectedChange, returnedChange?.firstChange)
+        }
+    }
+
+    @Test
+    @MediumTest
+    fun testMultipleDelegatedPointerEvents2() {
+        val events = mutableListOf<PointerEvent>()
+        val tag = "input rect"
+
+        val node = object : DelegatingNode() {
+            val piNode1 = delegate(SuspendingPointerInputModifierNode {
+                awaitPointerEventScope {
+                    events += awaitPointerEvent()
+                }
+            })
+
+            val piNode2 = delegate(SuspendingPointerInputModifierNode {
+                awaitPointerEventScope {
+                    events += awaitPointerEvent()
+                }
+            })
+        }
+
+        rule.setContent {
+            Box(
+                Modifier
+                    .fillMaxSize()
+                    .testTag(tag)
+                    .elementFor(node)
+            )
+        }
+
+        rule.onNodeWithTag(tag).performTouchInput {
+            down(Offset.Zero)
+        }
+        assertThat(events).hasSize(2)
+    }
 }
 
 private fun PointerInputChange.toPointerEvent() = PointerEvent(listOf(this))
@@ -751,7 +830,7 @@
     }
 
     override fun create() = instance
-    override fun update(node: Modifier.Node) = node
+    override fun update(node: Modifier.Node) {}
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is SuspendPointerInputModifierNodeElement) return false
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
index 2b9168f..06d53a3 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
@@ -30,7 +30,9 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.focusTarget
 import androidx.compose.ui.focus.setFocusableContent
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
@@ -89,6 +91,94 @@
     }
 
     @Test
+    fun delegated_androidWearCrownRotation_triggersRotaryEvent() {
+        val node = object : DelegatingNode() {
+            val rse = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+                override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    receivedEvent = event
+                    return true
+                }
+                override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    return false
+                }
+            })
+        }
+        // Arrange.
+        ContentWithInitialFocus {
+            Box(
+                modifier = Modifier
+                    .elementFor(node)
+                    .focusable(initiallyFocused = true)
+            )
+        }
+
+        // Act.
+        rule.runOnIdle {
+            rootView.dispatchGenericMotionEvent(
+                MotionEventBuilder.newBuilder()
+                    .setAction(ACTION_SCROLL)
+                    .setSource(SOURCE_ROTARY_ENCODER)
+                    .build()
+            )
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(receivedEvent).isNotNull()
+        }
+    }
+
+    @Test
+    fun delegated_multiple_androidWearCrownRotation_triggersRotaryEvent() {
+        var event1: RotaryScrollEvent? = null
+        var event2: RotaryScrollEvent? = null
+        val node = object : DelegatingNode() {
+            val a = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+                override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    event1 = event
+                    return false
+                }
+                override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    return false
+                }
+            })
+            val b = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+                override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    event2 = event
+                    return false
+                }
+                override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+                    return false
+                }
+            })
+        }
+        // Arrange.
+        ContentWithInitialFocus {
+            Box(
+                modifier = Modifier
+                    .elementFor(node)
+                    .focusable(initiallyFocused = true)
+            )
+        }
+
+        // Act.
+        rule.runOnIdle {
+            rootView.dispatchGenericMotionEvent(
+                MotionEventBuilder.newBuilder()
+                    .setAction(ACTION_SCROLL)
+                    .setSource(SOURCE_ROTARY_ENCODER)
+                    .build()
+            )
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(event1).isNotNull()
+            assertThat(event2).isNotNull()
+        }
+    }
+
+    @Test
     fun focusedItemReceivesHorizontalRotaryEvent() {
         // Arrange.
         ContentWithInitialFocus {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index 6d2ce4d..d0c65cd 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -41,11 +41,14 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
 import androidx.compose.ui.padding
 import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onRoot
@@ -546,6 +549,76 @@
     }
 
     @Test
+    fun delegatedGloballyPositionedNode() {
+        val paddingLeftPx = 100.0f
+        val paddingTopPx = 120.0f
+        var realLeft: Float? = null
+        var realTop: Float? = null
+
+        val positionedLatch = CountDownLatch(1)
+        val node = object : DelegatingNode() {
+            val ogp = delegate(
+                object : GlobalPositionAwareModifierNode, Modifier.Node() {
+                    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                        realLeft = coordinates.positionInParent().x
+                        realTop = coordinates.positionInParent().y
+                        positionedLatch.countDown()
+                    }
+                }
+            )
+        }
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
+                        .elementFor(node)
+                )
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        assertThat(paddingLeftPx).isEqualTo(realLeft)
+        assertThat(paddingTopPx).isEqualTo(realTop)
+    }
+
+    @Test
+    fun delegatedMultipleGloballyPositionedNodes() {
+        val paddingLeftPx = 100.0f
+        val paddingTopPx = 120.0f
+
+        val positionedLatch = CountDownLatch(2)
+        val node = object : DelegatingNode() {
+            val a = delegate(
+                object : GlobalPositionAwareModifierNode, Modifier.Node() {
+                    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                        positionedLatch.countDown()
+                    }
+                }
+            )
+            val b = delegate(
+                object : GlobalPositionAwareModifierNode, Modifier.Node() {
+                    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+                        positionedLatch.countDown()
+                    }
+                }
+            )
+        }
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
+                        .elementFor(node)
+                )
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+    }
+
+    @Test
     fun nestedLayoutCoordinates() {
         val firstPaddingPx = 10f
         val secondPaddingPx = 20f
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
index 250b916c..b825a77 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
@@ -24,10 +24,14 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.LayoutAwareModifierNode
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.elementFor
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SmallTest
@@ -269,4 +273,147 @@
         }
         assertNotEquals(Modifier.onSizeChanged(lambda1), Modifier.onSizeChanged(lambda2))
     }
+
+    @Test
+    @SmallTest
+    fun delegatedSizeChanged() {
+        var latch = CountDownLatch(1)
+        var changedSize = IntSize.Zero
+        var sizePx by mutableStateOf(10)
+        val node = object : DelegatingNode() {
+            val osc = delegate(
+                object : LayoutAwareModifierNode, Modifier.Node() {
+                    override fun onRemeasured(size: IntSize) {
+                        changedSize = size
+                        latch.countDown()
+                    }
+                }
+            )
+        }
+
+        rule.runOnUiThread {
+            activity.setContent {
+                with(LocalDensity.current) {
+                    Box(
+                        Modifier.padding(10.toDp()).elementFor(node)
+                    ) {
+                        Box(Modifier.requiredSize(sizePx.toDp()))
+                    }
+                }
+            }
+        }
+
+        // Initial setting will call onSizeChanged
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(10, changedSize.height)
+        assertEquals(10, changedSize.width)
+
+        latch = CountDownLatch(1)
+        sizePx = 20
+
+        // We've changed the size of the contents, so we should receive a onSizeChanged call
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(20, changedSize.height)
+        assertEquals(20, changedSize.width)
+    }
+
+    @Test
+    @SmallTest
+    fun multipleDelegatedSizeChanged() {
+        var latch = CountDownLatch(2)
+        var changedSize1 = IntSize.Zero
+        var changedSize2 = IntSize.Zero
+        var sizePx by mutableStateOf(10)
+        val node = object : DelegatingNode() {
+            val a = delegate(
+                object : LayoutAwareModifierNode, Modifier.Node() {
+                    override fun onRemeasured(size: IntSize) {
+                        changedSize1 = size
+                        latch.countDown()
+                    }
+                }
+            )
+            val b = delegate(
+                object : LayoutAwareModifierNode, Modifier.Node() {
+                    override fun onRemeasured(size: IntSize) {
+                        changedSize2 = size
+                        latch.countDown()
+                    }
+                }
+            )
+        }
+
+        rule.runOnUiThread {
+            activity.setContent {
+                with(LocalDensity.current) {
+                    Box(
+                        Modifier.padding(10.toDp()).elementFor(node)
+                    ) {
+                        Box(Modifier.requiredSize(sizePx.toDp()))
+                    }
+                }
+            }
+        }
+
+        // Initial setting will call onSizeChanged
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(10, changedSize1.height)
+        assertEquals(10, changedSize1.width)
+        assertEquals(10, changedSize2.height)
+        assertEquals(10, changedSize2.width)
+
+        latch = CountDownLatch(2)
+        sizePx = 20
+
+        // We've changed the size of the contents, so we should receive a onSizeChanged call
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(20, changedSize1.height)
+        assertEquals(20, changedSize1.width)
+        assertEquals(20, changedSize2.height)
+        assertEquals(20, changedSize2.width)
+    }
+
+    @Test
+    @SmallTest
+    fun multipleDelegatedOnPlaced() {
+        var latch = CountDownLatch(2)
+        var paddingDp by mutableStateOf(10)
+        val node = object : DelegatingNode() {
+            val a = delegate(
+                object : LayoutAwareModifierNode, Modifier.Node() {
+                    override fun onPlaced(coordinates: LayoutCoordinates) {
+                        latch.countDown()
+                    }
+                }
+            )
+            val b = delegate(
+                object : LayoutAwareModifierNode, Modifier.Node() {
+                    override fun onPlaced(coordinates: LayoutCoordinates) {
+                        latch.countDown()
+                    }
+                }
+            )
+        }
+
+        rule.runOnUiThread {
+            activity.setContent {
+                with(LocalDensity.current) {
+                    Box(
+                        Modifier.padding(paddingDp.toDp()).elementFor(node)
+                    ) {
+                        Box(Modifier.requiredSize(10.dp))
+                    }
+                }
+            }
+        }
+
+        // Initial setting will call onSizeChanged
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+
+        latch = CountDownLatch(2)
+        paddingDp = 20
+
+        // We've changed the size of the contents, so we should receive a onSizeChanged call
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+    }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/ResizingComposeViewTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/ResizingComposeViewTest.kt
index eaed850..1171a38 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/ResizingComposeViewTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/ResizingComposeViewTest.kt
@@ -396,8 +396,8 @@
     override fun create() = RemeasurementModifierNode().also {
         onRemeasurementAvailable(it)
     }
-    override fun update(node: RemeasurementModifierNode) = node.also {
-        onRemeasurementAvailable(it)
+    override fun update(node: RemeasurementModifierNode) {
+        onRemeasurementAvailable(node)
     }
     override fun hashCode(): Int = 242
     override fun equals(other: Any?) = other === this
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
index 3526180..a38a249 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
@@ -229,7 +229,7 @@
         override fun create() = fn()
         override fun hashCode() = System.identityHashCode(this)
         override fun equals(other: Any?) = other === this
-        override fun update(node: T) = node
+        override fun update(node: T) {}
     }
 
 class ConsumeInDrawNode : CompositionLocalConsumerModifierNode, DrawModifierNode, Modifier.Node() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
index 6409344..1c7d152 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
@@ -246,7 +246,7 @@
             override fun create(): Modifier.Node = nodeInstance
             override fun hashCode(): Int = System.identityHashCode(this)
             override fun equals(other: Any?) = (other === this)
-            override fun update(node: Modifier.Node) = nodeInstance
+            override fun update(node: Modifier.Node) { }
         }
 
         var active by mutableStateOf(true)
@@ -909,7 +909,7 @@
             }
         }
 
-        override fun update(node: Modifier.Node) = node.apply { onUpdate() }
+        override fun update(node: Modifier.Node) { onUpdate() }
 
         override fun hashCode(): Int = "ModifierNodeReuseAndDeactivationTest".hashCode()
 
@@ -929,9 +929,9 @@
 ) : ModifierNodeElement<StatelessModifierElement.Node>() {
     override fun create() = Node(size, onInvalidate)
 
-    override fun update(node: Node) = node.also {
-        it.size = size
-        it.onMeasure = onInvalidate
+    override fun update(node: Node) {
+        node.size = size
+        node.onMeasure = onInvalidate
     }
 
     class Node(var size: Int, var onMeasure: () -> Unit) : Modifier.Node(), LayoutModifierNode {
@@ -953,18 +953,18 @@
 ) : ModifierNodeElement<DelegatingModifierElement.Node>() {
     override fun create() = Node(onDelegatedNodeReset)
 
-    override fun update(node: Node) = node.also {
-        it.onReset = onDelegatedNodeReset
+    override fun update(node: Node) {
+        node.onReset = onDelegatedNodeReset
     }
 
     class Node(var onReset: () -> Unit) : DelegatingNode() {
-        private val inner = delegated {
+        private val inner = delegate(
             object : Modifier.Node() {
                 override fun onReset() {
                     [email protected]()
                 }
             }
-        }
+        )
     }
 }
 
@@ -973,8 +973,8 @@
 ) : ModifierNodeElement<LayerModifierElement.Node>() {
     override fun create() = Node(layerBlock)
 
-    override fun update(node: Node) = node.also {
-        it.layerBlock = layerBlock
+    override fun update(node: Node) {
+        node.layerBlock = layerBlock
     }
 
     class Node(var layerBlock: () -> Unit) : Modifier.Node(), LayoutModifierNode {
@@ -997,8 +997,8 @@
 ) : ModifierNodeElement<ObserverModifierElement.Node>() {
     override fun create() = Node(observedBlock)
 
-    override fun update(node: Node) = node.also {
-        it.observedBlock = observedBlock
+    override fun update(node: Node) {
+        node.observedBlock = observedBlock
     }
 
     class Node(var observedBlock: () -> Unit) : Modifier.Node(), ObserverNode {
@@ -1024,8 +1024,8 @@
 ) : ModifierNodeElement<LayoutModifierElement.Node>() {
     override fun create() = Node(measureBlock)
 
-    override fun update(node: Node) = node.also {
-        it.measureBlock = measureBlock
+    override fun update(node: Node) {
+        node.measureBlock = measureBlock
     }
 
     class Node(var measureBlock: () -> Unit) : Modifier.Node(), LayoutModifierNode {
@@ -1062,8 +1062,8 @@
 ) : ModifierNodeElement<DrawModifierElement.Node>() {
     override fun create() = Node(drawBlock)
 
-    override fun update(node: Node) = node.also {
-        it.drawBlock = drawBlock
+    override fun update(node: Node) {
+        node.drawBlock = drawBlock
     }
 
     class Node(var drawBlock: () -> Unit) : Modifier.Node(), DrawModifierNode {
@@ -1084,7 +1084,7 @@
 
 private object StatelessLayoutElement1 : ModifierNodeElement<StatelessLayoutModifier1>() {
     override fun create() = StatelessLayoutModifier1()
-    override fun update(node: StatelessLayoutModifier1) = node
+    override fun update(node: StatelessLayoutModifier1) {}
     override fun hashCode(): Int = 241
     override fun equals(other: Any?) = other === this
 }
@@ -1103,7 +1103,7 @@
 
 private object StatelessLayoutElement2 : ModifierNodeElement<StatelessLayoutModifier2>() {
     override fun create() = StatelessLayoutModifier2()
-    override fun update(node: StatelessLayoutModifier2) = node
+    override fun update(node: StatelessLayoutModifier2) {}
     override fun hashCode(): Int = 242
     override fun equals(other: Any?) = other === this
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalConsumerModifierNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalConsumerModifierNodeTest.kt
index 8fa9d75..451605f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalConsumerModifierNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalConsumerModifierNodeTest.kt
@@ -304,7 +304,7 @@
         crossinline create: () -> T
     ): ModifierNodeElement<T> = object : ModifierNodeElement<T>() {
         override fun create(): T = create()
-        override fun update(node: T) = node
+        override fun update(node: T) {}
         override fun hashCode(): Int = System.identityHashCode(this)
         override fun equals(other: Any?) = (other === this)
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
index 081c512..fe89fe6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
@@ -513,7 +513,7 @@
 
         // Act.
         val child = rule.runOnIdle {
-            node1.firstChild(Nodes.Any)
+            node1.child
         }
 
         // Assert.
@@ -534,27 +534,7 @@
 
         // Act.
         val child = rule.runOnIdle {
-            node1.firstChild(Nodes.Any)
-        }
-
-        // Assert.
-        assertThat(child).isEqualTo(node2)
-    }
-
-    @Test
-    fun firstChild_differentLayoutNode() {
-        // Arrange.
-        val (node1, node2, node3) = List(3) { object : Modifier.Node() {} }
-        rule.setContent {
-            Box(modifier = modifierElementOf { node1 }) {
-                Box(modifier = modifierElementOf { node2 }
-                    .then(modifierElementOf { node3 }))
-            }
-        }
-
-        // Act.
-        val child = rule.runOnIdle {
-            node1.firstChild(Nodes.Any)
+            node1.child
         }
 
         // Assert.
@@ -576,7 +556,7 @@
 
         // Act.
         val child = rule.runOnIdle {
-            node1.firstChild(Nodes.Any)
+            node1.child
         }
 
         // Assert.
@@ -586,9 +566,9 @@
     @Test
     fun delegatedNodeGetsCoordinator() {
         val node = object : DelegatingNode() {
-            val inner = delegated {
+            val inner = delegate(
                 object : Modifier.Node() { }
-            }
+            )
         }
 
         rule.setContent {
@@ -613,7 +593,7 @@
         val factory: () -> T
     ) : ModifierNodeElement<T>() {
         override fun create(): T = factory()
-        override fun update(node: T) = node
+        override fun update(node: T) {}
         override fun InspectorInfo.inspectableProperties() {
             name = "testNode"
         }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
index ad0f352..2e3d335 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
@@ -152,7 +152,7 @@
         override fun create() = object : Modifier.Node() {}
             .apply<Modifier.Node>(onCreate)
 
-        override fun update(node: Modifier.Node) = node
+        override fun update(node: Modifier.Node) {}
 
         override fun InspectorInfo.inspectableProperties() {
             name = "Invalidate Subtree Modifier.Node"
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeCoroutineScopeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeCoroutineScopeTest.kt
index 625885d..2c3e809 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeCoroutineScopeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeCoroutineScopeTest.kt
@@ -58,7 +58,7 @@
 
         val testElement = object : ModifierNodeElement<TestNode>() {
             override fun create(): TestNode = TestNode()
-            override fun update(node: TestNode): TestNode = node
+            override fun update(node: TestNode) {}
 
             override fun hashCode(): Int = 0
             override fun equals(other: Any?): Boolean = other === this
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
index 29cafc1..a875d116 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
@@ -253,7 +253,7 @@
     val node: T
 ) : ModifierNodeElement<T>() {
     override fun create(): T = node
-    override fun update(node: T): T = this.node
+    override fun update(node: T) {}
     override fun InspectorInfo.inspectableProperties() {
         name = modifierName
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ObserverNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ObserverNodeTest.kt
index 1049aaf..620cc57 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ObserverNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ObserverNodeTest.kt
@@ -170,7 +170,7 @@
         val node: T
     ) : ModifierNodeElement<T>() {
         override fun create(): T = node
-        override fun update(node: T) = this.node
+        override fun update(node: T) {}
         override fun InspectorInfo.inspectableProperties() {
             name = "testNode"
         }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index 8b7397d..bdbd7fc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -35,6 +35,8 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.MeasurePolicy
 import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -306,6 +308,23 @@
     }
 
     @Test
+    fun higherUpSemanticsOverridePropertiesOfLowerSemanticsOnSameNode() {
+        rule.setContent {
+            Box(Modifier
+                .testTag("tag")
+                .semantics { contentDescription = "high" }
+                .semantics { contentDescription = "low" }
+            )
+        }
+
+        rule
+            .onNodeWithTag("tag")
+            .assert(
+                SemanticsMatcher.expectValue(SemanticsProperties.ContentDescription, listOf("high"))
+            )
+    }
+
+    @Test
     fun replacedChildren_includeFakeNodes() {
         val tag = "tag1"
         rule.setContent {
@@ -868,6 +887,52 @@
             root.children[1].config.getOrNull(SemanticsProperties.TestTag)
         )
     }
+
+    @Test
+    fun delegatedSemanticsPropertiesGetRead() {
+        val node = object : DelegatingNode() {
+            val inner = delegate(SemanticsMod {
+                contentDescription = "hello world"
+            })
+        }
+
+        rule.setContent {
+            Box(
+                Modifier
+                    .testTag(TestTag)
+                    .elementFor(node)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TestTag)
+            .assertContentDescriptionEquals("hello world")
+    }
+
+    @Test
+    fun multipleDelegatesGetCombined() {
+        val node = object : DelegatingNode() {
+            val a = delegate(SemanticsMod {
+                contentDescription = "hello world"
+            })
+            val b = delegate(SemanticsMod {
+                testProperty = "bar"
+            })
+        }
+
+        rule.setContent {
+            Box(
+                Modifier
+                    .testTag(TestTag)
+                    .elementFor(node)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TestTag)
+            .assertContentDescriptionEquals("hello world")
+            .assertTestPropertyEquals("bar")
+    }
 }
 
 private fun SemanticsNodeInteraction.assertDoesNotHaveProperty(property: SemanticsPropertyKey<*>) {
@@ -877,9 +942,9 @@
 private val TestProperty = SemanticsPropertyKey<String>("TestProperty") { parent, child ->
     if (parent == null) child else "$parent, $child"
 }
-private var SemanticsPropertyReceiver.testProperty by TestProperty
+internal var SemanticsPropertyReceiver.testProperty by TestProperty
 
-private fun SemanticsNodeInteraction.assertTestPropertyEquals(value: String) = assert(
+internal fun SemanticsNodeInteraction.assertTestPropertyEquals(value: String) = assert(
     SemanticsMatcher.expectValue(TestProperty, value)
 )
 
@@ -976,3 +1041,24 @@
 }
 
 private enum class TestSlot { First, Second }
+
+internal fun SemanticsMod(
+    mergeDescendants: Boolean = false,
+    properties: SemanticsPropertyReceiver.() -> Unit
+): CoreSemanticsModifierNode {
+    return CoreSemanticsModifierNode(
+        SemanticsConfiguration().apply {
+            isMergingSemanticsOfDescendants = mergeDescendants
+            properties()
+        }
+    )
+}
+
+internal fun Modifier.elementFor(node: Modifier.Node): Modifier {
+    return this then NodeElement(node)
+}
+
+internal data class NodeElement(val node: Modifier.Node) : ModifierNodeElement<Modifier.Node>() {
+    override fun create(): Modifier.Node = node
+    override fun update(node: Modifier.Node) {}
+}
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 3b68870..5497180 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
@@ -119,6 +119,7 @@
 import androidx.compose.ui.node.LayoutNode.UsageByParent
 import androidx.compose.ui.node.LayoutNodeDrawScope
 import androidx.compose.ui.node.MeasureAndLayoutDelegate
+import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.OwnedLayer
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
@@ -127,7 +128,6 @@
 import androidx.compose.ui.semantics.EmptySemanticsModifierNodeElement
 import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.semantics.findClosestParentNode
-import androidx.compose.ui.semantics.outerSemantics
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.font.Font
@@ -739,7 +739,7 @@
                 ) {
                     super.onInitializeAccessibilityNodeInfo(host, info)
                     var parentId = layoutNode
-                        .findClosestParentNode { it.outerSemantics != null }
+                        .findClosestParentNode { it.nodes.has(Nodes.Semantics) }
                         ?.semanticsId
                     if (parentId == null ||
                         parentId == semanticsOwner.unmergedRootSemanticsNode.id
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index 8eaf240..5ded9eb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -56,9 +56,8 @@
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.HitTestResult
 import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.OwnerScope
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.collapsedSemanticsConfiguration
 import androidx.compose.ui.node.requireLayoutNode
 import androidx.compose.ui.platform.accessibility.hasCollectionInfo
 import androidx.compose.ui.platform.accessibility.setCollectionInfo
@@ -76,8 +75,8 @@
 import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.semantics.SemanticsPropertiesAndroid
+import androidx.compose.ui.semantics.collapsedSemantics
 import androidx.compose.ui.semantics.getOrNull
-import androidx.compose.ui.semantics.outerSemantics
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.InternalTextApi
@@ -2032,24 +2031,24 @@
     internal fun hitTestSemanticsAt(x: Float, y: Float): Int {
         view.measureAndLayout()
 
-        val hitSemanticsEntities = HitTestResult<SemanticsModifierNode>()
+        val hitSemanticsEntities = HitTestResult()
         view.root.hitTestSemantics(
             pointerPosition = Offset(x, y),
             hitSemanticsEntities = hitSemanticsEntities
         )
 
-        val wrapper = hitSemanticsEntities.lastOrNull()?.requireLayoutNode()?.outerSemantics
+        val layoutNode = hitSemanticsEntities.lastOrNull()?.requireLayoutNode()
+
         var virtualViewId = InvalidId
-        if (wrapper != null) {
+        if (layoutNode?.nodes?.has(Nodes.Semantics) == true) {
 
             // The node below is not added to the tree; it's a wrapper around outer semantics to
             // use the methods available to the SemanticsNode
-            val semanticsNode = SemanticsNode(wrapper, false)
+            val semanticsNode = SemanticsNode(layoutNode, false)
 
             // Do not 'find' invisible nodes when exploring by touch. This will prevent us from
             // sending events for invisible nodes
             if (semanticsNode.isVisible) {
-                val layoutNode = wrapper.requireLayoutNode()
                 val androidView = view
                     .androidViewsHandler
                     .layoutNodeToHolder[layoutNode]
@@ -2212,17 +2211,18 @@
             return
         }
         // When we finally send the event, make sure it is an accessibility-focusable node.
-        var semanticsWrapper = layoutNode.outerSemantics
-            ?: layoutNode.findClosestParentNode { it.outerSemantics != null }
-                ?.outerSemantics ?: return
-        if (!semanticsWrapper.collapsedSemanticsConfiguration().isMergingSemanticsOfDescendants) {
-            layoutNode.findClosestParentNode {
-                it.outerSemantics
-                    ?.collapsedSemanticsConfiguration()
-                    ?.isMergingSemanticsOfDescendants == true
-            }?.outerSemantics?.let { semanticsWrapper = it }
+        var semanticsNode = if (layoutNode.nodes.has(Nodes.Semantics))
+                layoutNode
+            else
+                layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
+
+        val config = semanticsNode?.collapsedSemantics ?: return
+        if (!config.isMergingSemanticsOfDescendants) {
+            semanticsNode.findClosestParentNode {
+                it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
+            }?.let { semanticsNode = it }
         }
-        val id = semanticsWrapper.requireLayoutNode().semanticsId
+        val id = semanticsNode?.semanticsId ?: return
         if (!subtreeChangedSemanticsNodesIds.add(id)) {
             return
         }
@@ -2376,8 +2376,7 @@
                                 )
                                 // Here we use the merged node
                                 @OptIn(ExperimentalComposeUiApi::class)
-                                val mergedNode =
-                                    SemanticsNode(newNode.outerSemanticsNode, true)
+                                val mergedNode = newNode.copyWithMergingEnabled()
                                 val contentDescription = mergedNode.config.getOrNull(
                                     SemanticsProperties.ContentDescription
                                 )?.fastJoinToString(",")
@@ -3288,14 +3287,12 @@
     // text nodes that are part of the 'merged' text field, for example hint or label.
     val ancestor = layoutNode.findClosestParentNode {
         // looking for text field merging node
-        val ancestorSemanticsConfiguration = it.outerSemantics?.collapsedSemanticsConfiguration()
+        val ancestorSemanticsConfiguration = it.collapsedSemantics
         ancestorSemanticsConfiguration?.isMergingSemanticsOfDescendants == true &&
             ancestorSemanticsConfiguration.contains(SemanticsActions.SetText)
     }
     return ancestor != null &&
-        ancestor.outerSemantics
-            ?.collapsedSemanticsConfiguration()
-            ?.getOrNull(SemanticsProperties.Focused) != true
+        ancestor.collapsedSemantics?.getOrNull(SemanticsProperties.Focused) != true
 }
 
 private fun AccessibilityAction<*>.accessibilityEquals(other: Any?): Boolean {
@@ -3350,11 +3347,12 @@
         ) {
             return
         }
+        val touchBoundsInRoot = currentNode.touchBoundsInRoot
         val boundsInRoot = android.graphics.Rect(
-            currentNode.touchBoundsInRoot.left.roundToInt(),
-            currentNode.touchBoundsInRoot.top.roundToInt(),
-            currentNode.touchBoundsInRoot.right.roundToInt(),
-            currentNode.touchBoundsInRoot.bottom.roundToInt(),
+            touchBoundsInRoot.left.roundToInt(),
+            touchBoundsInRoot.top.roundToInt(),
+            touchBoundsInRoot.right.roundToInt(),
+            touchBoundsInRoot.bottom.roundToInt(),
         )
         val region = Region().also { it.set(boundsInRoot) }
         val virtualViewId = if (currentNode.id == root.id) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
index a1872aa..cf111e5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
@@ -304,7 +304,7 @@
     val map: CompositionLocalMap
 ) : ModifierNodeElement<CompositionLocalMapInjectionNode>() {
     override fun create() = CompositionLocalMapInjectionNode(map)
-    override fun update(node: CompositionLocalMapInjectionNode) = node.also { it.map = map }
+    override fun update(node: CompositionLocalMapInjectionNode) { node.map = map }
     override fun hashCode(): Int = map.hashCode()
     override fun equals(other: Any?): Boolean {
         return other is CompositionLocalMapInjectionElement && other.map == map
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
index 8c3731a..919acfe 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
@@ -19,9 +19,11 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
 import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.node.ModifierNodeOwnerScope
 import androidx.compose.ui.node.NodeCoordinator
 import androidx.compose.ui.node.NodeKind
+import androidx.compose.ui.node.invalidateDraw
 import androidx.compose.ui.node.requireOwner
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -214,6 +216,24 @@
         var isAttached: Boolean = false
             private set
 
+        /**
+         * If this property returns `true`, then nodes will be automatically invalidated after the
+         * modifier update completes (For example, if the returned Node is a [DrawModifierNode], its
+         * [DrawModifierNode.invalidateDraw] function will be invoked automatically as part of
+         * auto invalidation).
+         *
+         * This is enabled by default, and provides a convenient mechanism to schedule invalidation
+         * and apply changes made to the modifier. You may choose to set this to `false` if your
+         * modifier has auto-invalidatable properties that do not frequently require invalidation to
+         * improve performance by skipping unnecessary invalidation. If `autoInvalidate` is set to
+         * `false`, you must call the appropriate invalidate functions manually when the modifier
+         * is updated or else the updates may not be reflected in the UI appropriately.
+         */
+        @Suppress("GetterSetterNames")
+        @get:Suppress("GetterSetterNames")
+        open val shouldAutoInvalidate: Boolean
+            get() = true
+
         internal open fun updateCoordinator(coordinator: NodeCoordinator?) {
             this.coordinator = coordinator
         }
@@ -226,7 +246,6 @@
             check(coordinator != null)
             isAttached = true
             onAttach()
-            // TODO(lmr): run side effects?
         }
 
         internal open fun detach() {
@@ -239,8 +258,6 @@
                 it.cancel()
                 scope = null
             }
-            // coordinator = null
-            // TODO(lmr): cancel jobs / side effects?
         }
 
         internal open fun reset() {
@@ -288,6 +305,7 @@
          *
          * This API can only be called if the node [isAttached].
          */
+        @ExperimentalComposeUiApi
         fun sideEffect(effect: () -> Unit) {
             requireOwner().registerOnEndApplyChangesListener(effect)
         }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
index d44f338..30f3372 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
@@ -42,8 +42,8 @@
 
 internal data class ZIndexElement(val zIndex: Float) : ModifierNodeElement<ZIndexModifier>() {
     override fun create() = ZIndexModifier(zIndex)
-    override fun update(node: ZIndexModifier) = node.also {
-        it.zIndex = zIndex
+    override fun update(node: ZIndexModifier) {
+        node.zIndex = zIndex
     }
     override fun InspectorInfo.inspectableProperties() {
         name = "zIndex"
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 1f3306f..aecd204 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
@@ -100,8 +100,8 @@
 ) : ModifierNodeElement<DrawBackgroundModifier>() {
     override fun create() = DrawBackgroundModifier(onDraw)
 
-    override fun update(node: DrawBackgroundModifier) = node.apply {
-        onDraw = [email protected]
+    override fun update(node: DrawBackgroundModifier) {
+        node.onDraw = onDraw
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -111,7 +111,7 @@
 }
 
 @OptIn(ExperimentalComposeUiApi::class)
-private class DrawBackgroundModifier(
+internal class DrawBackgroundModifier(
     var onDraw: DrawScope.() -> Unit
 ) : Modifier.Node(), DrawModifierNode {
 
@@ -148,8 +148,8 @@
         return CacheDrawNode(CacheDrawScope(), onBuildDrawCache)
     }
 
-    override fun update(node: CacheDrawNode) = node.apply {
-        block = onBuildDrawCache
+    override fun update(node: CacheDrawNode) {
+        node.block = onBuildDrawCache
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -281,8 +281,8 @@
 ) : ModifierNodeElement<DrawWithContentModifier>() {
     override fun create() = DrawWithContentModifier(onDraw)
 
-    override fun update(node: DrawWithContentModifier) = node.apply {
-        onDraw = [email protected]
+    override fun update(node: DrawWithContentModifier) {
+        node.onDraw = onDraw
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
index f2a353b..a62e2c3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
@@ -38,7 +38,7 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.invalidateDraw
 import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
@@ -78,11 +78,6 @@
 /**
  * Customized [ModifierNodeElement] for painting content using [painter].
  *
- * IMPORTANT NOTE: This class sets [androidx.compose.ui.node.ModifierNodeElement.autoInvalidate]
- * to false which means it MUST invalidate both draw and the layout. It invalidates both in the
- * [PainterModifierNodeElement.update] method through [LayoutModifierNode.invalidateLayer]
- * (invalidates draw) and [LayoutModifierNode.invalidateMeasurements] (invalidates measure).
- *
  * @param painter used to paint content
  * @param sizeToIntrinsics `true` to size the element relative to [Painter.intrinsicSize]
  * @param alignment specifies alignment of the [painter] relative to content
@@ -100,9 +95,6 @@
     val alpha: Float,
     val colorFilter: ColorFilter?
 ) : ModifierNodeElement<PainterModifierNode>() {
-    override val autoInvalidate: Boolean
-        get() = false
-
     override fun create(): PainterModifierNode {
         return PainterModifierNode(
             painter = painter,
@@ -114,7 +106,7 @@
         )
     }
 
-    override fun update(node: PainterModifierNode): PainterModifierNode {
+    override fun update(node: PainterModifierNode) {
         val intrinsicsChanged = node.sizeToIntrinsics != sizeToIntrinsics ||
             (sizeToIntrinsics && node.painter.intrinsicSize != painter.intrinsicSize)
 
@@ -127,12 +119,10 @@
 
         // Only remeasure if intrinsics have changed.
         if (intrinsicsChanged) {
-            node.invalidateMeasurements()
+            node.invalidateMeasurement()
         }
         // redraw because one of the node properties has changed.
         node.invalidateDraw()
-
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -149,6 +139,12 @@
 /**
  * [DrawModifier] used to draw the provided [Painter] followed by the contents
  * of the component itself
+ *
+ *
+ * IMPORTANT NOTE: This class sets [androidx.compose.ui.Modifier.Node.shouldAutoInvalidate]
+ * to false which means it MUST invalidate both draw and the layout. It invalidates both in the
+ * [PainterModifierNodeElement.update] method through [LayoutModifierNode.invalidateLayer]
+ * (invalidates draw) and [LayoutModifierNode.invalidateLayout] (invalidates layout).
  */
 @OptIn(ExperimentalComposeUiApi::class)
 private class PainterModifierNode(
@@ -168,6 +164,9 @@
     private val useIntrinsicSize: Boolean
         get() = sizeToIntrinsics && painter.intrinsicSize.isSpecified
 
+    override val shouldAutoInvalidate: Boolean
+        get() = false
+
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt
index 1dd3153..5719fe3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt
@@ -39,8 +39,8 @@
 ) : ModifierNodeElement<FocusChangedModifierNode>() {
     override fun create() = FocusChangedModifierNode(onFocusChanged)
 
-    override fun update(node: FocusChangedModifierNode) = node.apply {
-        onFocusChanged = [email protected]
+    override fun update(node: FocusChangedModifierNode) {
+        node.onFocusChanged = onFocusChanged
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
index f38261a..f0bc4aa 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
@@ -46,8 +46,8 @@
 ) : ModifierNodeElement<FocusEventModifierNodeImpl>() {
     override fun create() = FocusEventModifierNodeImpl(onFocusEvent)
 
-    override fun update(node: FocusEventModifierNodeImpl) = node.apply {
-        onFocusEvent = [email protected]
+    override fun update(node: FocusEventModifierNodeImpl) {
+        node.onFocusEvent = onFocusEvent
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
index 12fd0c7..1fb8667 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
@@ -22,8 +22,9 @@
 import androidx.compose.ui.focus.FocusStateImpl.Inactive
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitAncestors
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.requireOwner
+import androidx.compose.ui.node.visitSelfAndAncestors
+import androidx.compose.ui.node.visitSelfAndChildren
 
 /**
  * Implement this interface create a modifier node that can be used to observe focus state changes
@@ -38,14 +39,18 @@
     fun onFocusEvent(focusState: FocusState)
 }
 
+internal fun FocusEventModifierNode.invalidateFocusEvent() {
+    requireOwner().focusOwner.scheduleInvalidation(this)
+}
+
 internal fun FocusEventModifierNode.getFocusState(): FocusState {
-    visitChildren(Nodes.FocusTarget) {
+    visitSelfAndChildren(Nodes.FocusTarget) {
         when (val focusState = it.focusStateImpl) {
             // If we find a focused child, we use that child's state as the aggregated state.
             Active, ActiveParent, Captured -> return focusState
             // We use the Inactive state only if we don't have a focused child.
             // ie. we ignore this child if another child provides aggregated state.
-            Inactive -> return@visitChildren
+            Inactive -> return@visitSelfAndChildren
         }
     }
     return Inactive
@@ -58,13 +63,8 @@
  * Make this public after [FocusTargetModifierNode] is made public.
  */
 internal fun FocusTargetModifierNode.refreshFocusEventNodes() {
-    visitAncestors(Nodes.FocusEvent or Nodes.FocusTarget) {
-        // If we reach the previous focus target node, we have gone too far, as
-        //  this is applies to the another focus event.
-        if (it.isKind(Nodes.FocusTarget)) return
-
+    visitSelfAndAncestors(Nodes.FocusEvent, untilType = Nodes.FocusTarget) {
         // TODO(251833873): Consider caching it.getFocusState().
-        check(it is FocusEventModifierNode)
         it.onFocusEvent(it.getFocusState())
     }
 }
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 6217e96..6268fa8 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
@@ -16,17 +16,15 @@
 
 package androidx.compose.ui.focus
 
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.focus.FocusStateImpl.Inactive
 import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.visitSelfAndChildren
 
 /**
  * The [FocusInvalidationManager] allows us to schedule focus related nodes for invalidation.
  * These nodes are invalidated after onApplyChanges. It does this by registering an
  * onApplyChangesListener when nodes are scheduled for invalidation.
  */
-@OptIn(ExperimentalComposeUiApi::class)
 internal class FocusInvalidationManager(
     private val onRequestApplyChangesListener: (() -> Unit) -> Unit
 ) {
@@ -62,7 +60,7 @@
     private val invalidateNodes: () -> Unit = {
         // Process all the invalidated FocusProperties nodes.
         focusPropertiesNodes.forEach {
-            it.visitChildren(Nodes.FocusTarget) { focusTarget ->
+            it.visitSelfAndChildren(Nodes.FocusTarget) { focusTarget ->
                 focusTargetNodes.add(focusTarget)
             }
         }
@@ -84,7 +82,7 @@
             var requiresUpdate = true
             var aggregatedNode = false
             var focusTarget: FocusTargetModifierNode? = null
-            focusEventNode.visitChildren(Nodes.FocusTarget) {
+            focusEventNode.visitSelfAndChildren(Nodes.FocusTarget) {
 
                 // If there are multiple focus targets associated with this focus event node,
                 // we need to calculate the aggregated state.
@@ -101,7 +99,7 @@
                 if (focusTargetNodes.contains(it)) {
                     requiresUpdate = false
                     focusTargetsWithInvalidatedFocusEvents.add(it)
-                    return@visitChildren
+                    return@visitSelfAndChildren
                 }
             }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
index 638594b..6f5912d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
@@ -33,13 +33,13 @@
 import androidx.compose.ui.focus.FocusStateImpl.Inactive
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.KeyInputModifierNode
 import androidx.compose.ui.input.rotary.RotaryScrollEvent
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.NodeKind
 import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.ancestors
+import androidx.compose.ui.node.dispatchForKind
 import androidx.compose.ui.node.nearestAncestor
 import androidx.compose.ui.node.visitLocalChildren
 import androidx.compose.ui.platform.InspectorInfo
@@ -65,7 +65,7 @@
     override val modifier: Modifier = object : ModifierNodeElement<FocusTargetModifierNode>() {
         override fun create() = rootFocusNode
 
-        override fun update(node: FocusTargetModifierNode) = node
+        override fun update(node: FocusTargetModifierNode) {}
 
         override fun InspectorInfo.inspectableProperties() {
             name = "RootFocusTarget"
@@ -185,7 +185,7 @@
             "Event can't be processed because we do not have an active focus target."
         }
         val focusedKeyInputNode = activeFocusTarget.lastLocalKeyInputNode()
-            ?: activeFocusTarget.nearestAncestor(Nodes.KeyInput)
+            ?: activeFocusTarget.nearestAncestor(Nodes.KeyInput)?.node
 
         focusedKeyInputNode?.traverseAncestors(
             type = Nodes.KeyInput,
@@ -236,15 +236,15 @@
         focusInvalidationManager.scheduleInvalidation(node)
     }
 
-    private inline fun <reified T : DelegatableNode> T.traverseAncestors(
+    private inline fun <reified T : DelegatableNode> DelegatableNode.traverseAncestors(
         type: NodeKind<T>,
         onPreVisit: (T) -> Unit,
         onVisit: (T) -> Unit
     ) {
         val ancestors = ancestors(type)
         ancestors?.fastForEachReversed(onPreVisit)
-        onPreVisit(this)
-        onVisit(this)
+        node.dispatchForKind(type, onPreVisit)
+        node.dispatchForKind(type, onVisit)
         ancestors?.fastForEach(onVisit)
     }
 
@@ -255,12 +255,11 @@
         return rootFocusNode.findActiveFocusNode()?.focusRect()
     }
 
-    private fun DelegatableNode.lastLocalKeyInputNode(): KeyInputModifierNode? {
-        var focusedKeyInputNode: KeyInputModifierNode? = null
+    private fun DelegatableNode.lastLocalKeyInputNode(): Modifier.Node? {
+        var focusedKeyInputNode: Modifier.Node? = null
         visitLocalChildren(Nodes.FocusTarget or Nodes.KeyInput) { modifierNode ->
             if (modifierNode.isKind(Nodes.FocusTarget)) return focusedKeyInputNode
 
-            check(modifierNode is KeyInputModifierNode)
             focusedKeyInputNode = modifierNode
         }
         return focusedKeyInputNode
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
index 8404c04..d9acf72 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
@@ -187,8 +187,8 @@
 ) : ModifierNodeElement<FocusPropertiesModifierNodeImpl>() {
     override fun create() = FocusPropertiesModifierNodeImpl(scope)
 
-    override fun update(node: FocusPropertiesModifierNodeImpl) = node.apply {
-        focusPropertiesScope = scope
+    override fun update(node: FocusPropertiesModifierNodeImpl) {
+        node.focusPropertiesScope = scope
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
index bf18f73..fd7f579 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.requireOwner
 
 /**
  * Implement this interface create a modifier node that can be used to modify the focus properties
@@ -31,3 +32,7 @@
      */
     fun modifyFocusProperties(focusProperties: FocusProperties)
 }
+
+internal fun FocusPropertiesModifierNode.invalidateFocusProperties() {
+    requireOwner().focusOwner.scheduleInvalidation(this)
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
index dd6fd9f..c8ab894 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
@@ -55,10 +55,10 @@
 ) : ModifierNodeElement<FocusRequesterModifierNodeImpl>() {
     override fun create() = FocusRequesterModifierNodeImpl(focusRequester)
 
-    override fun update(node: FocusRequesterModifierNodeImpl) = node.apply {
-        focusRequester.focusRequesterNodes -= this
-        focusRequester = [email protected]
-        focusRequester.focusRequesterNodes += this
+    override fun update(node: FocusRequesterModifierNodeImpl) {
+        node.focusRequester.focusRequesterNodes -= node
+        node.focusRequester = focusRequester
+        node.focusRequester.focusRequesterNodes += node
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
index a24d056..5b32dc9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
@@ -20,7 +20,7 @@
 import androidx.compose.ui.focus.FocusDirection.Companion.Enter
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.visitSelfAndChildren
 
 /**
  * Implement this interface to create a modifier node that can be used to request changes in
@@ -37,7 +37,7 @@
  */
 @OptIn(ExperimentalComposeUiApi::class)
 fun FocusRequesterModifierNode.requestFocus(): Boolean {
-    visitChildren(Nodes.FocusTarget) { focusTarget ->
+    visitSelfAndChildren(Nodes.FocusTarget) { focusTarget ->
         val focusProperties = focusTarget.fetchFocusProperties()
         return if (focusProperties.canFocus) {
             focusTarget.requestFocus()
@@ -66,7 +66,7 @@
  * @sample androidx.compose.ui.samples.CaptureFocusSample
  */
 fun FocusRequesterModifierNode.captureFocus(): Boolean {
-    visitChildren(Nodes.FocusTarget) {
+    visitSelfAndChildren(Nodes.FocusTarget) {
         if (it.captureFocus()) {
             return true
         }
@@ -89,7 +89,7 @@
  * @sample androidx.compose.ui.samples.CaptureFocusSample
  */
 fun FocusRequesterModifierNode.freeFocus(): Boolean {
-    visitChildren(Nodes.FocusTarget) {
+    visitSelfAndChildren(Nodes.FocusTarget) {
         if (it.freeFocus()) return true
     }
     return false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
index 5b5c194..456b959 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
@@ -29,9 +29,11 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.ObserverNode
+import androidx.compose.ui.node.dispatchForKind
 import androidx.compose.ui.node.observeReads
 import androidx.compose.ui.node.requireOwner
 import androidx.compose.ui.node.visitAncestors
+import androidx.compose.ui.node.visitSelfAndAncestors
 import androidx.compose.ui.platform.InspectorInfo
 
 /**
@@ -83,13 +85,7 @@
      */
     internal fun fetchFocusProperties(): FocusProperties {
         val properties = FocusPropertiesImpl()
-        visitAncestors(Nodes.FocusProperties or Nodes.FocusTarget) {
-            // If we reach the previous default focus properties node, we have gone too far, as
-            //  this is applies to the parent focus modifier.
-            if (it.isKind(Nodes.FocusTarget)) return properties
-
-            // Parent can override any values set by this
-            check(it is FocusPropertiesModifierNode)
+        visitSelfAndAncestors(Nodes.FocusProperties, untilType = Nodes.FocusTarget) {
             it.modifyFocusProperties(properties)
         }
         return properties
@@ -169,18 +165,30 @@
     }
 
     internal fun scheduleInvalidationForFocusEvents() {
+        // include possibility for ourselves to also be a focus event modifier node in case
+        // we are being delegated to
+        node.dispatchForKind(Nodes.FocusEvent) { eventNode ->
+            eventNode.invalidateFocusEvent()
+        }
+        // Since this is potentially called while _this_ node is getting detached, it is possible
+        // that the nodes above us are already detached, thus, we check for isAttached here.
+        // We should investigate changing the order that children.detach() is called relative to
+        // actually nulling out / detaching ones self.
         visitAncestors(Nodes.FocusEvent or Nodes.FocusTarget) {
             if (it.isKind(Nodes.FocusTarget)) return@visitAncestors
 
-            check(it is FocusEventModifierNode)
-            requireOwner().focusOwner.scheduleInvalidation(it)
+            if (it.isAttached) {
+                it.dispatchForKind(Nodes.FocusEvent) { eventNode ->
+                    eventNode.invalidateFocusEvent()
+                }
+            }
         }
     }
 
     internal object FocusTargetModifierElement : ModifierNodeElement<FocusTargetModifierNode>() {
         override fun create() = FocusTargetModifierNode()
 
-        override fun update(node: FocusTargetModifierNode) = node
+        override fun update(node: FocusTargetModifierNode) {}
 
         override fun InspectorInfo.inspectableProperties() {
             name = "focusTarget"
@@ -190,3 +198,7 @@
         override fun equals(other: Any?) = other === this
     }
 }
+
+internal fun FocusTargetModifierNode.invalidateFocusTarget() {
+    requireOwner().focusOwner.scheduleInvalidation(this)
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
index ba495f2..267b71f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
@@ -399,12 +399,6 @@
     val spotShadowColor: Color,
     val compositingStrategy: CompositingStrategy
 ) : ModifierNodeElement<SimpleGraphicsLayerModifier>() {
-
-    /**
-     * [SimpleGraphicsLayerModifier.invalidateLayerBlock] is doing the manual invalidation.
-     */
-    override val autoInvalidate = false
-
     override fun create(): SimpleGraphicsLayerModifier {
         return SimpleGraphicsLayerModifier(
             scaleX = scaleX,
@@ -427,7 +421,7 @@
         )
     }
 
-    override fun update(node: SimpleGraphicsLayerModifier): SimpleGraphicsLayerModifier {
+    override fun update(node: SimpleGraphicsLayerModifier) {
         node.scaleX = scaleX
         node.scaleY = scaleY
         node.alpha = alpha
@@ -446,8 +440,6 @@
         node.spotShadowColor = spotShadowColor
         node.compositingStrategy = compositingStrategy
         node.invalidateLayerBlock()
-
-        return node
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -549,18 +541,11 @@
 private data class BlockGraphicsLayerElement(
     val block: GraphicsLayerScope.() -> Unit
 ) : ModifierNodeElement<BlockGraphicsLayerModifier>() {
-
-    /**
-     * We can skip remeasuring as we only need to rerun the placement block. we request it
-     * manually in the [update] block.
-     */
-    override val autoInvalidate = false
-
     override fun create() = BlockGraphicsLayerModifier(block)
 
-    override fun update(node: BlockGraphicsLayerModifier) = node.apply {
-        layerBlock = block
-        invalidateLayerBlock()
+    override fun update(node: BlockGraphicsLayerModifier) {
+        node.layerBlock = block
+        node.invalidateLayerBlock()
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -573,6 +558,12 @@
     var layerBlock: GraphicsLayerScope.() -> Unit,
 ) : LayoutModifierNode, Modifier.Node() {
 
+    /**
+     * We can skip remeasuring as we only need to rerun the placement block. we request it
+     * manually in the update block.
+     */
+    override val shouldAutoInvalidate: Boolean get() = false
+
     fun invalidateLayerBlock() {
         requireCoordinator(Nodes.Layout).wrapped?.updateLayerBlock(
             layerBlock,
@@ -615,6 +606,12 @@
     var compositingStrategy: CompositingStrategy = CompositingStrategy.Auto
 ) : LayoutModifierNode, Modifier.Node() {
 
+    /**
+     * We can skip remeasuring as we only need to rerun the placement block. we request it
+     * manually in the update block.
+     */
+    override val shouldAutoInvalidate: Boolean get() = false
+
     private var layerBlock: GraphicsLayerScope.() -> Unit = {
         scaleX = [email protected]
         scaleY = [email protected]
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
index b823385..3d26c05 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
@@ -56,9 +56,9 @@
 ) : ModifierNodeElement<KeyInputModifierNodeImpl>() {
     override fun create() = KeyInputModifierNodeImpl(onKeyEvent, onPreKeyEvent)
 
-    override fun update(node: KeyInputModifierNodeImpl) = node.apply {
-        onEvent = onKeyEvent
-        onPreEvent = onPreKeyEvent
+    override fun update(node: KeyInputModifierNodeImpl) {
+        node.onEvent = onKeyEvent
+        node.onPreEvent = onPreKeyEvent
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt
index 88fc93a..be03189 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt
@@ -72,9 +72,9 @@
         onPreEvent = onPreKeyEvent
     )
 
-    override fun update(node: InterceptedKeyInputModifierNodeImpl) = node.apply {
-        onEvent = onKeyEvent
-        onPreEvent = onPreKeyEvent
+    override fun update(node: InterceptedKeyInputModifierNodeImpl) {
+        node.onEvent = onKeyEvent
+        node.onPreEvent = onPreKeyEvent
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
index 1bf2e8c..2d61ab5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
@@ -342,10 +342,8 @@
         return NestedScrollNode(connection, dispatcher)
     }
 
-    override fun update(node: NestedScrollNode): NestedScrollNode {
-        node.connection = connection
-        node.updateDispatcher(dispatcher)
-        return node
+    override fun update(node: NestedScrollNode) {
+        node.updateNode(connection, dispatcher)
     }
 
     override fun hashCode(): Int {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
index 093b26d..f1282fb 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
@@ -16,24 +16,37 @@
 
 package androidx.compose.ui.input.nestedscroll
 
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.modifier.ModifierLocalMap
 import androidx.compose.ui.modifier.ModifierLocalNode
 import androidx.compose.ui.modifier.modifierLocalMapOf
 import androidx.compose.ui.modifier.modifierLocalOf
-import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.unit.Velocity
 import kotlinx.coroutines.CoroutineScope
 
 internal val ModifierLocalNestedScroll = modifierLocalOf<NestedScrollNode?> { null }
 
 /**
+ * This creates a Nested Scroll Modifier node that can be delegated to. In most case you should
+ * use [Modifier.nestedScroll] since that implementation also uses this. Use this factory to create
+ * nodes that can be delegated to.
+ */
+fun nestedScrollModifierNode(
+    connection: NestedScrollConnection,
+    dispatcher: NestedScrollDispatcher?
+): DelegatableNode {
+    return NestedScrollNode(connection, dispatcher)
+}
+
+/**
  * NestedScroll using ModifierLocal as implementation.
  */
 internal class NestedScrollNode(
     var connection: NestedScrollConnection,
     dispatcher: NestedScrollDispatcher?
-) : ModifierLocalNode, NestedScrollConnection, DelegatingNode() {
+) : ModifierLocalNode, NestedScrollConnection, DelegatableNode, Modifier.Node() {
 
     // Resolved dispatcher for re-use in case of null dispatcher is passed.
     private var resolvedDispatcher: NestedScrollDispatcher
@@ -46,7 +59,7 @@
         get() = if (isAttached) ModifierLocalNestedScroll.current else null
 
     private val parentConnection: NestedScrollConnection?
-        get() = ModifierLocalNestedScroll.current
+        get() = if (isAttached) ModifierLocalNestedScroll.current else null
 
     override val providedValues: ModifierLocalMap
         get() = modifierLocalMapOf(ModifierLocalNestedScroll to this)
@@ -99,7 +112,7 @@
     }
 
     // On receiving a new dispatcher, re-setting fields
-    fun updateDispatcher(newDispatcher: NestedScrollDispatcher?) {
+    private fun updateDispatcher(newDispatcher: NestedScrollDispatcher?) {
         resetDispatcherFields() // Reset fields of current dispatcher.
 
         // Update dispatcher associated with this node.
@@ -139,4 +152,12 @@
     private fun resetDispatcherFields() {
         resolvedDispatcher.modifierLocalNode = null
     }
+
+    internal fun updateNode(
+        connection: NestedScrollConnection,
+        dispatcher: NestedScrollDispatcher?
+    ) {
+        this.connection = connection
+        updateDispatcher(dispatcher)
+    }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
index bf51c11..9a7ddac 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
@@ -19,10 +19,11 @@
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.node.InternalCoreApi
-import androidx.compose.ui.node.PointerInputModifierNode
-import androidx.compose.ui.node.isAttached
+import androidx.compose.ui.node.Nodes
+import androidx.compose.ui.node.dispatchForKind
 import androidx.compose.ui.node.layoutCoordinates
 import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
@@ -34,7 +35,6 @@
  * @property rootCoordinates the root [LayoutCoordinates] that [PointerInputChange]s will be
  * relative to.
  */
-@OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
 internal class HitPathTracker(private val rootCoordinates: LayoutCoordinates) {
 
     /*@VisibleForTesting*/
@@ -52,14 +52,14 @@
      * @param pointerInputNodes The [PointerInputFilter]s that were hit by [pointerId].  Must be
      * ordered from ancestor to descendant.
      */
-    fun addHitPath(pointerId: PointerId, pointerInputNodes: List<PointerInputModifierNode>) {
+    fun addHitPath(pointerId: PointerId, pointerInputNodes: List<Modifier.Node>) {
         var parent: NodeParent = root
         var merging = true
         eachPin@ for (i in pointerInputNodes.indices) {
             val pointerInputNode = pointerInputNodes[i]
             if (merging) {
                 val node = parent.children.firstOrNull {
-                    it.pointerInputNode == pointerInputNode
+                    it.modifierNode == pointerInputNode
                 }
                 if (node != null) {
                     node.markIsIn()
@@ -226,7 +226,7 @@
         var index = 0
         while (index < children.size) {
             val child = children[index]
-            if (!child.pointerInputNode.isAttached) {
+            if (!child.modifierNode.isAttached) {
                 children.removeAt(index)
                 child.dispatchCancel()
             } else {
@@ -252,7 +252,7 @@
  */
 /*@VisibleForTesting*/
 @OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
-internal class Node(val pointerInputNode: PointerInputModifierNode) : NodeParent() {
+internal class Node(val modifierNode: Modifier.Node) : NodeParent() {
 
     // Note: this is essentially a set, and writes should be guarded accordingly. We use a
     // MutableVector here instead since a set ends up being quite heavy, and calls to
@@ -293,10 +293,12 @@
             val event = pointerEvent!!
             val size = coordinates!!.size
             // Dispatch on the tunneling pass.
-            pointerInputNode.onPointerEvent(event, PointerEventPass.Initial, size)
+            modifierNode.dispatchForKind(Nodes.PointerInput) {
+                it.onPointerEvent(event, PointerEventPass.Initial, size)
+            }
 
             // Dispatch to children.
-            if (pointerInputNode.isAttached) {
+            if (modifierNode.isAttached) {
                 children.forEach {
                     it.dispatchMainEventPass(
                         // Pass only the already-filtered and position-translated changes down to
@@ -309,9 +311,11 @@
                 }
             }
 
-            if (pointerInputNode.isAttached) {
+            if (modifierNode.isAttached) {
                 // Dispatch on the bubbling pass.
-                pointerInputNode.onPointerEvent(event, PointerEventPass.Main, size)
+                modifierNode.dispatchForKind(Nodes.PointerInput) {
+                    it.onPointerEvent(event, PointerEventPass.Main, size)
+                }
             }
         }
     }
@@ -327,10 +331,12 @@
             val event = pointerEvent!!
             val size = coordinates!!.size
             // Dispatch on the tunneling pass.
-            pointerInputNode.onPointerEvent(event, PointerEventPass.Final, size)
+            modifierNode.dispatchForKind(Nodes.PointerInput) {
+                it.onPointerEvent(event, PointerEventPass.Final, size)
+            }
 
             // Dispatch to children.
-            if (pointerInputNode.isAttached) {
+            if (modifierNode.isAttached) {
                 children.forEach { it.dispatchFinalEventPass(internalPointerEvent) }
             }
         }
@@ -362,9 +368,11 @@
             )
 
         // Avoid future work if we know this node will no-op
-        if (!pointerInputNode.isAttached) return true
+        if (!modifierNode.isAttached) return true
 
-        coordinates = pointerInputNode.layoutCoordinates
+        modifierNode.dispatchForKind(Nodes.PointerInput) {
+            coordinates = it.layoutCoordinates
+        }
 
         @OptIn(ExperimentalComposeUiApi::class)
         for ((key, change) in changes) {
@@ -475,7 +483,7 @@
     }
 
     /**
-     * Calls [block] if there are relevant changes, and if [pointerInputNode] is attached
+     * Calls [block] if there are relevant changes, and if [modifierNode] is attached
      *
      * @return whether [block] was called
      */
@@ -485,7 +493,7 @@
         // If there are no relevant changes, there is nothing to process so return false.
         if (relevantChanges.isEmpty()) return false
         // If the input filter is not attached, avoid dispatching
-        if (!pointerInputNode.isAttached) return false
+        if (!modifierNode.isAttached) return false
 
         block()
 
@@ -502,7 +510,9 @@
      */
     override fun dispatchCancel() {
         children.forEach { it.dispatchCancel() }
-        pointerInputNode.onCancelPointerInput()
+        modifierNode.dispatchForKind(Nodes.PointerInput) {
+            it.onCancelPointerInput()
+        }
     }
 
     fun markIsIn() {
@@ -531,7 +541,7 @@
     }
 
     override fun toString(): String {
-        return "Node(pointerInputFilter=$pointerInputNode, children=$children, " +
+        return "Node(pointerInputFilter=$modifierNode, children=$children, " +
             "pointerIds=$pointerIds)"
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
index d9e1f91..dd43434 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
@@ -21,7 +21,6 @@
 import androidx.compose.ui.node.HitTestResult
 import androidx.compose.ui.node.InternalCoreApi
 import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.util.fastForEach
 
 internal interface PositionCalculator {
@@ -32,12 +31,11 @@
 /**
  * The core element that receives [PointerInputEvent]s and process them in Compose UI.
  */
-@OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
 internal class PointerInputEventProcessor(val root: LayoutNode) {
 
     private val hitPathTracker = HitPathTracker(root.coordinates)
     private val pointerInputChangeEventProducer = PointerInputChangeEventProducer()
-    private val hitResult = HitTestResult<PointerInputModifierNode>()
+    private val hitResult = HitTestResult()
 
     /**
      * [process] doesn't currently support reentrancy. This prevents reentrant calls
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 3df0795..3685a4c 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
@@ -319,10 +319,8 @@
         return SuspendingPointerInputModifierNodeImpl(pointerInputHandler)
     }
 
-    override fun update(node: SuspendingPointerInputModifierNodeImpl):
-        SuspendingPointerInputModifierNodeImpl {
+    override fun update(node: SuspendingPointerInputModifierNodeImpl) {
         node.pointerInputHandler = pointerInputHandler
-        return node
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
index 6b2351c..9b5bd47 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
@@ -83,9 +83,9 @@
         onPreEvent = onPreRotaryScrollEvent
     )
 
-    override fun update(node: RotaryInputModifierNodeImpl) = node.apply {
-        onEvent = onRotaryScrollEvent
-        onPreEvent = onPreRotaryScrollEvent
+    override fun update(node: RotaryInputModifierNodeImpl) {
+        node.onEvent = onRotaryScrollEvent
+        node.onPreEvent = onPreRotaryScrollEvent
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
index c51cdcc..c73c968 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
@@ -37,8 +37,8 @@
 ) : ModifierNodeElement<LayoutIdModifier>() {
     override fun create() = LayoutIdModifier(layoutId)
 
-    override fun update(node: LayoutIdModifier): LayoutIdModifier = node.also {
-        it.layoutId = layoutId
+    override fun update(node: LayoutIdModifier) {
+        node.layoutId = layoutId
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -52,7 +52,7 @@
  * will act as parent data, and can be used for example by parent layouts to associate
  * composable children to [Measurable]s when doing layout, as shown below.
  */
-private class LayoutIdModifier(
+internal class LayoutIdModifier(
     layoutId: Any,
 ) : ParentDataModifierNode, LayoutIdParentData, Modifier.Node() {
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
index e9f36f0..d96e68e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
@@ -274,8 +274,8 @@
 ) : ModifierNodeElement<LayoutModifierImpl>() {
     override fun create() = LayoutModifierImpl(measure)
 
-    override fun update(node: LayoutModifierImpl) = node.apply {
-        measureBlock = measure
+    override fun update(node: LayoutModifierImpl) {
+        node.measureBlock = measure
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
index 298b719..ccdd77b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
@@ -156,8 +156,9 @@
     ) -> MeasureResult,
 ) : ModifierNodeElement<IntermediateLayoutModifierNode>() {
     override fun create() = IntermediateLayoutModifierNode(measure)
-    override fun update(node: IntermediateLayoutModifierNode): IntermediateLayoutModifierNode =
-        node.apply { this.measureBlock = measure }
+    override fun update(node: IntermediateLayoutModifierNode) {
+        node.measureBlock = measure
+    }
 
     override fun InspectorInfo.inspectableProperties() {
         name = "intermediateLayout"
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnGloballyPositionedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnGloballyPositionedModifier.kt
index 96790aa..d6ee7f8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnGloballyPositionedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnGloballyPositionedModifier.kt
@@ -61,10 +61,9 @@
         return onGloballyPositioned.hashCode()
     }
 
-    override fun update(node: OnGloballyPositionedNode): OnGloballyPositionedNode =
-        node.also {
-            it.callback = onGloballyPositioned
-        }
+    override fun update(node: OnGloballyPositionedNode) {
+        node.callback = onGloballyPositioned
+    }
 
     override fun InspectorInfo.inspectableProperties() {
         name = "onGloballyPositioned"
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnPlacedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnPlacedModifier.kt
index 909f5ab..c4acf63 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnPlacedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnPlacedModifier.kt
@@ -40,8 +40,8 @@
 ) : ModifierNodeElement<OnPlacedNode>() {
     override fun create() = OnPlacedNode(callback = onPlaced)
 
-    override fun update(node: OnPlacedNode) = node.apply {
-        callback = onPlaced
+    override fun update(node: OnPlacedNode) {
+        node.callback = onPlaced
     }
 
     override fun InspectorInfo.inspectableProperties() {
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 bb271ac..e326bec 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
@@ -28,7 +28,7 @@
  * [Modifier.Node] implements this interface, in practice any [Modifier.Node] can be delegated.
  *
  * @see DelegatingNode
- * @see DelegatingNode.delegated
+ * @see DelegatingNode.delegate
  */
 // TODO(lmr): this interface needs a better name
 interface DelegatableNode {
@@ -40,6 +40,8 @@
     val node: Modifier.Node
 }
 
+internal val DelegatableNode.isDelegationRoot: Boolean get() = node === this
+
 // TREE TRAVERSAL APIS
 // For now, traversing the node tree and layout node tree will be kept out of public API.
 // Some internal modifiers, such as Focus, PointerInput, etc. will all need to utilize this
@@ -69,12 +71,16 @@
     return null
 }
 
-internal inline fun DelegatableNode.visitAncestors(mask: Int, block: (Modifier.Node) -> Unit) {
+internal inline fun DelegatableNode.visitAncestors(
+    mask: Int,
+    includeSelf: Boolean = false,
+    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. Although that might only be
     //  necessary for visiting subtree.
     check(node.isAttached)
-    var node: Modifier.Node? = node.parent
+    var node: Modifier.Node? = if (includeSelf) node else node.parent
     var layout: LayoutNode? = requireLayoutNode()
     while (layout != null) {
         val head = layout.nodes.head
@@ -91,28 +97,6 @@
     }
 }
 
-internal fun DelegatableNode.ancestors(mask: Int): List<Modifier.Node>? {
-    check(node.isAttached)
-    var ancestors: MutableList<Modifier.Node>? = null
-    var node: Modifier.Node? = node.parent
-    var layout: LayoutNode? = requireLayoutNode()
-    while (layout != null) {
-        val head = layout.nodes.head
-        if (head.aggregateChildKindSet and mask != 0) {
-            while (node != null) {
-                if (node.kindSet and mask != 0) {
-                    if (ancestors == null) ancestors = mutableListOf()
-                    ancestors += node
-                }
-                node = node.parent
-            }
-        }
-        layout = layout.parent
-        node = layout?.nodes?.tail
-    }
-    return ancestors
-}
-
 internal fun DelegatableNode.nearestAncestor(mask: Int): Modifier.Node? {
     check(node.isAttached)
     var node: Modifier.Node? = node.parent
@@ -277,52 +261,83 @@
     type: NodeKind<T>,
     block: (T) -> Unit
 ) = visitLocalChildren(type.mask) {
-    if (it is T) block(it)
+    it.dispatchForKind(type, block)
 }
 
 internal inline fun <reified T> DelegatableNode.visitLocalParents(
     type: NodeKind<T>,
     block: (T) -> Unit
 ) = visitLocalParents(type.mask) {
-    if (it is T) block(it)
+    it.dispatchForKind(type, block)
 }
 
-internal inline fun <reified T> DelegatableNode.localParent(type: NodeKind<T>): T? =
-    localParent(type.mask) as? T
-
-internal inline fun <reified T> DelegatableNode.localChild(type: NodeKind<T>): T? =
-    localChild(type.mask) as? T
-
 internal inline fun <reified T> DelegatableNode.visitAncestors(
     type: NodeKind<T>,
+    includeSelf: Boolean = false,
     block: (T) -> Unit
-) = visitAncestors(type.mask) { if (it is T) block(it) }
+) = visitAncestors(type.mask, includeSelf) { it.dispatchForKind(type, block) }
 
-@Suppress("UNCHECKED_CAST") // Type info lost due to erasure.
+internal inline fun <reified T> DelegatableNode.visitSelfAndAncestors(
+    type: NodeKind<T>,
+    untilType: NodeKind<*>,
+    block: (T) -> Unit
+) {
+    val self = node
+    visitAncestors(type.mask or untilType.mask, true) {
+        if (it !== self && it.isKind(untilType)) return
+        if (it.isKind(type)) {
+            it.dispatchForKind(type, block)
+        }
+    }
+}
+
 internal inline fun <reified T> DelegatableNode.ancestors(
     type: NodeKind<T>
-): List<T>? = ancestors(type.mask) as? List<T>
+): List<T>? {
+    var result: MutableList<T>? = null
+    visitAncestors(type) {
+        val list = if (result == null) {
+            mutableListOf<T>().also { result = it }
+        } else result!!
+        list += it
+    }
+    return result
+}
 
-internal inline fun <reified T : Any> DelegatableNode.nearestAncestor(type: NodeKind<T>): T? =
-    nearestAncestor(type.mask) as? T
-
-internal inline fun <reified T : Any> DelegatableNode.firstChild(type: NodeKind<T>): T? =
-    firstChild(type.mask) as? T
+internal inline fun <reified T : Any> DelegatableNode.nearestAncestor(type: NodeKind<T>): T? {
+    visitAncestors(type) {
+        return it
+    }
+    return null
+}
 
 internal inline fun <reified T> DelegatableNode.visitSubtree(
     type: NodeKind<T>,
     block: (T) -> Unit
-) = visitSubtree(type.mask) { if (it is T) block(it) }
+) = visitSubtree(type.mask) { it.dispatchForKind(type, block) }
 
 internal inline fun <reified T> DelegatableNode.visitChildren(
     type: NodeKind<T>,
     block: (T) -> Unit
-) = visitChildren(type.mask) { if (it is T) block(it) }
+) = visitChildren(type.mask) { it.dispatchForKind(type, block) }
+
+internal inline fun <reified T> DelegatableNode.visitSelfAndChildren(
+    type: NodeKind<T>,
+    block: (T) -> Unit
+) {
+    node.dispatchForKind(type, block)
+    visitChildren(type.mask) { it.dispatchForKind(type, block) }
+}
 
 internal inline fun <reified T> DelegatableNode.visitSubtreeIf(
     type: NodeKind<T>,
     block: (T) -> Boolean
-) = visitSubtreeIf(type.mask) { if (it is T) block(it) else true }
+) = visitSubtreeIf(type.mask) foo@{
+    it.dispatchForKind(type) {
+        if (!block(it)) return@foo false
+    }
+    true
+}
 
 internal fun DelegatableNode.has(type: NodeKind<*>): Boolean =
     node.aggregateChildKindSet and type.mask != 0
@@ -367,4 +382,89 @@
     if (node.isAttached) {
         requireLayoutNode().invalidateSubtree()
     }
+}
+
+// It is safe to do this for LayoutModifierNode because we enforce only a single delegate is
+// a LayoutModifierNode, however for other NodeKinds that is not true. As a result, this function
+// is not generic and instead is made specific to LayoutModifierNode.
+internal fun Modifier.Node.asLayoutModifierNode(): LayoutModifierNode? {
+    if (!isKind(Nodes.Layout)) return null
+    if (this is LayoutModifierNode) return this
+    if (this is DelegatingNode) {
+        var node: Modifier.Node? = delegate
+        while (node != null) {
+            if (node is LayoutModifierNode) return node
+            node = if (node is DelegatingNode && node.isKind(Nodes.Layout)) {
+                // NOTE: we can only do this here because we are enforcing that a delegating node
+                // only behaves as a single LayoutModifierNode, not multiple, so we know that if
+                // the node is of kind "Layout", then one of its delegates has to be a
+                // LayoutModifierNode and *none of the other delegates of its parent can be*. As a
+                // result, we can avoid allocating a collection here and instead just dive down into
+                // this delegate directly.
+                node.delegate
+            } else {
+                node.child
+            }
+        }
+    }
+    return null
+}
+
+/**
+ * Since Modifier.Nodes can have multiple delegates of the same type, generally we should use this
+ * method in lieu of casting a modifier.node to a particular NodeKind's interface type. This will
+ * allow us to properly perform operations on the right delegates for a given node instance.
+ *
+ * If a Node implements T, then this will just be called once. if it does NOT implement T, it will
+ * effectively dispatch recursively (although this is implemented iteratively) to all of its direct
+ * delegates where delegate.isKind(kind) is true.
+ *
+ * In the common case of the node implementing the type directly, this method will not allocate,
+ * however it allocates a vector if it dispatches to delegates.
+ */
+internal inline fun <reified T> Modifier.Node.dispatchForKind(
+    kind: NodeKind<T>,
+    block: (T) -> Unit
+) {
+    var stack: MutableVector<Modifier.Node>? = null
+    var node: Modifier.Node? = this
+    while (node != null) {
+        if (node is T) {
+            block(node)
+        } else if (node.isKind(kind) && node is DelegatingNode) {
+            // We jump through a few extra hoops here to avoid the vector allocation in the
+            // case where there is only one delegate node that implements this particular kind.
+            // It is very likely that a delegating node will have one or zero delegates of a
+            // particular kind, so this seems like a worthwhile optimization to make.
+            var count = 0
+            node.forEachImmediateDelegate { next ->
+                if (next.isKind(kind)) {
+                    count++
+                    if (count == 1) {
+                        node = next
+                    } else {
+                        // turns out there are multiple delegates that implement this kind, so we
+                        // have to allocate in this case.
+                        stack = stack ?: mutableVectorOf()
+                        val theNode = node
+                        if (theNode != null) {
+                            stack?.add(theNode)
+                            node = null
+                        }
+                        stack?.add(next)
+                    }
+                }
+            }
+            if (count == 1) {
+                // if count == 1 then `node` is pointing to the "next" node we need to look at
+                continue
+            }
+        }
+        node = stack.pop()
+    }
+}
+
+private fun MutableVector<Modifier.Node>?.pop(): Modifier.Node? {
+    return if (this == null || isEmpty()) null
+    else removeAt(size - 1)
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
index d8ae34d..b011798e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
@@ -17,82 +17,244 @@
 package androidx.compose.ui.node
 
 import androidx.compose.ui.Modifier
+import org.jetbrains.annotations.TestOnly
 
 /**
  * A [Modifier.Node] which is able to delegate work to other [Modifier.Node] instances.
  *
  * This can be useful to compose multiple node implementations into one.
  *
- * @sample androidx.compose.ui.samples.DelegatedNodeSample
+ * @sample androidx.compose.ui.samples.DelegatedNodeSampleExplicit
+ * @sample androidx.compose.ui.samples.DelegatedNodeSampleImplicit
+ * @sample androidx.compose.ui.samples.LazyDelegationExample
+ * @sample androidx.compose.ui.samples.ConditionalDelegationExample
+ * @sample androidx.compose.ui.samples.DelegateInAttachSample
  *
  * @see DelegatingNode
  */
 abstract class DelegatingNode : Modifier.Node() {
+
+    /**
+     * This is the kindSet of the node if it had no delegates. This will never change, but kindSet
+     * might, so we cache this value to be able to more efficiently recalculate the kindSet
+     */
+    @Suppress("LeakingThis")
+    internal val selfKindSet: Int = calculateNodeKindSetFrom(this)
     override fun updateCoordinator(coordinator: NodeCoordinator?) {
         super.updateCoordinator(coordinator)
-        forEachDelegate {
+        forEachImmediateDelegate {
             it.updateCoordinator(coordinator)
         }
     }
 
-    private var delegate: Modifier.Node? = null
+    internal var delegate: Modifier.Node? = null
+
+    @TestOnly
+    internal fun <T : DelegatableNode> delegateUnprotected(delegatableNode: T): T =
+        delegate(delegatableNode)
+    @TestOnly
+    internal fun undelegateUnprotected(instance: DelegatableNode) = undelegate(instance)
 
     /**
      * In order to properly delegate work to another [Modifier.Node], the delegated instance must
-     * be created and returned inside of a [delegated] call. Doing this will
+     * be created and returned inside of a [delegate] call. Doing this will
      * ensure that the created node instance follows all of the right lifecycles and is properly
      * discoverable in this position of the node tree.
      *
-     * By using [delegated], the [fn] parameter is executed synchronously, and the result is
-     * returned from this function for immediate use.
+     * By using [delegate], the [delegatableNode] parameter is returned from this function for
+     * convenience.
      *
      * This method can be called from within an `init` block, however the returned delegated node
-     * will not be attached until the delegating node is attached. If [delegated] is called after
+     * will not be attached until the delegating node is attached. If [delegate] is called after
      * the delegating node is already attached, the returned delegated node will be attached.
      */
-    fun <T : Modifier.Node> delegated(fn: () -> T): T {
-        val owner = node
-        val delegate = fn()
-        delegate.setAsDelegateTo(owner)
+    protected fun <T : DelegatableNode> delegate(delegatableNode: T): T {
+        val delegateNode = delegatableNode.node
+        val isAlreadyDelegated = delegateNode !== delegatableNode
+        if (isAlreadyDelegated) {
+            val delegator = (delegatableNode as? Modifier.Node)?.parent
+            val isDelegatedToThisNode = delegateNode === node && delegator == this
+            if (isDelegatedToThisNode) {
+                // nothing left to do
+                return delegatableNode
+            } else {
+                error("Cannot delegate to an already delegated node")
+            }
+        }
+        check(!delegateNode.isAttached) {
+            "Cannot delegate to an already attached node"
+        }
+        // this could be a delegate itself, so we make sure to setAsDelegateTo(node) instead of
+        // setAsDelegateTo(this).
+        delegateNode.setAsDelegateTo(node)
+        val beforeKindSet = kindSet
+        // need to include the delegate's delegates in the calculation
+        val delegatedKindSet = calculateNodeKindSetFromIncludingDelegates(delegateNode)
+        delegateNode.kindSet = delegatedKindSet
+        validateDelegateKindSet(delegatedKindSet, delegateNode)
+
+        // We store the delegates of a node as a singly-linked-list, with the "head" as `delegate`
+        // and the next pointer as `child`.
+        delegateNode.child = delegate
+        delegate = delegateNode
+
+        // for a delegate, parent always points to the node which delegated to it
+        delegateNode.parent = this
+        updateNodeKindSet(kindSet or delegatedKindSet, recalculateOwner = false)
+
         if (isAttached) {
-            updateCoordinator(owner.coordinator)
-            delegate.attach()
+            if (Nodes.Layout in delegatedKindSet && Nodes.Layout !in beforeKindSet) {
+                // We delegated to a layout modifier. In this case, we need to ensure that a new
+                // NodeCoordinator gets created for this node
+                val chain = requireLayoutNode().nodes
+                node.updateCoordinator(null)
+                chain.syncCoordinators()
+            } else {
+                updateCoordinator(coordinator)
+            }
+            delegateNode.attach()
+            autoInvalidateInsertedNode(delegateNode)
         }
-        addDelegate(delegate)
-        return delegate
+        return delegatableNode
     }
 
-    private fun addDelegate(node: Modifier.Node) {
-        val tail = delegate
-        if (tail != null) {
-            node.parent = tail
+    /**
+     * This function expects a node which was passed in to [delegate] for this node, and is
+     * currently being delegated to to be passed in as [instance]. After this function returns, the
+     * node will no longer be attached, and will not be an active delegate of this node.
+     *
+     * If [instance] is not an active delegate of this node, this function will throw an
+     * [IllegalStateException].
+     */
+    protected fun undelegate(instance: DelegatableNode) {
+        var prev: Modifier.Node? = null
+        var it: Modifier.Node? = delegate
+        var found = false
+        while (it != null) {
+            if (it === instance) {
+                // remove from delegate chain
+                if (it.isAttached) {
+                    autoInvalidateRemovedNode(it)
+                    it.detach()
+                }
+                it.setAsDelegateTo(it) // sets "node" back to itself
+                it.aggregateChildKindSet = 0
+                if (prev == null) {
+                    this.delegate = it.child
+                } else {
+                    prev.child = it.child
+                }
+                it.child = null
+                it.parent = null
+                found = true
+                break
+            }
+            prev = it
+            it = it.child
         }
-        delegate = node
+        if (found) {
+            val beforeKindSet = kindSet
+            val afterKindSet = calculateNodeKindSetFromIncludingDelegates(this)
+            updateNodeKindSet(afterKindSet, recalculateOwner = true)
+
+            if (isAttached && Nodes.Layout in beforeKindSet && Nodes.Layout !in afterKindSet) {
+                // the delegate getting removed was a layout delegate. As a result, we need
+                // to sync coordinators
+                val chain = requireLayoutNode().nodes
+                node.updateCoordinator(null)
+                chain.syncCoordinators()
+            }
+        } else {
+            error("Could not find delegate: $instance")
+        }
     }
 
-    private inline fun forEachDelegate(block: (Modifier.Node) -> Unit) {
+    private fun validateDelegateKindSet(delegateKindSet: Int, delegateNode: Modifier.Node) {
+        val current = kindSet
+        if (Nodes.Layout in delegateKindSet && Nodes.Layout in current) {
+            // at this point, we know that the node was _already_ a layout modifier, and we are
+            // delegating to another layout modifier. In order to properly handle this, we need
+            // to require that the delegating node is itself a LayoutModifierNode to ensure that
+            // they are explicitly handling the combination. If not, we throw, since
+            check(this is LayoutModifierNode) {
+                "Delegating to multiple LayoutModifierNodes without the delegating node " +
+                    "implementing LayoutModifierNode itself is not allowed." +
+                    "\nDelegating Node: $this" +
+                    "\nDelegate Node: $delegateNode"
+            }
+        }
+    }
+
+    private fun updateNodeKindSet(newKindSet: Int, recalculateOwner: Boolean) {
+        val before = kindSet
+        kindSet = newKindSet
+        if (before != newKindSet) {
+            var agg = newKindSet
+            if (isDelegationRoot) {
+                aggregateChildKindSet = agg
+            }
+            // if we changed, then we must update our aggregateChildKindSet of ourselves and
+            // everything up the spine
+
+            if (isAttached) {
+                val owner = node
+                var it: Modifier.Node? = this
+                // first we traverse up the delegate tree until we hit the "owner" node, which is
+                // the node which is actually part of the tree, ie the "root delegating node".
+                // As we iterate here, we update the aggregateChildKindSet as well as the kindSet,
+                // since a delegating node takes on the kinds of the nodes it delegates to.
+                while (it != null) {
+                    agg = it.kindSet or agg
+                    it.kindSet = agg
+                    if (it === owner) break
+                    it = it.parent
+                }
+
+                if (recalculateOwner && it === owner) {
+                    agg = calculateNodeKindSetFromIncludingDelegates(owner)
+                    owner.kindSet = agg
+                }
+
+                agg = agg or (it?.child?.aggregateChildKindSet ?: 0)
+
+                // Now we are traversing the spine of nodes in the actual tree, so we update the
+                // aggregateChildKindSet here, but not the kindSets.
+                while (it != null) {
+                    agg = it.kindSet or agg
+                    it.aggregateChildKindSet = agg
+                    it = it.parent
+                }
+            }
+        }
+    }
+
+    internal inline fun forEachImmediateDelegate(block: (Modifier.Node) -> Unit) {
         var node: Modifier.Node? = delegate
         while (node != null) {
             block(node)
-            node = node.parent
+            node = node.child
         }
     }
 
     override fun attach() {
         super.attach()
-        forEachDelegate {
+        forEachImmediateDelegate {
             it.updateCoordinator(coordinator)
-            it.attach()
+            // NOTE: it might already be attached if the delegate was delegated to inside of
+            // onAttach()
+            if (!it.isAttached) {
+                it.attach()
+            }
         }
     }
 
     override fun detach() {
-        forEachDelegate { it.detach() }
+        forEachImmediateDelegate { it.detach() }
         super.detach()
     }
 
     override fun reset() {
         super.reset()
-        forEachDelegate { it.reset() }
+        forEachImmediateDelegate { it.reset() }
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
index 0c2ede1..c41e989 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.node
 
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackInt2
 import kotlin.math.sign
@@ -30,7 +31,7 @@
  * @see LayoutNode.hitTest
  * @see NodeCoordinator.hitTest
  */
-internal class HitTestResult<T> : List<T> {
+internal class HitTestResult : List<Modifier.Node> {
     private var values = arrayOfNulls<Any>(16)
     // contains DistanceAndInLayer
     private var distanceFromEdgeAndInLayer = LongArray(16)
@@ -92,7 +93,7 @@
      * Records [node] as a hit, adding it to the [HitTestResult] or replacing the existing one.
      * Runs [childHitTest] to do further hit testing for children.
      */
-    fun hit(node: T, isInLayer: Boolean, childHitTest: () -> Unit) {
+    fun hit(node: Modifier.Node, isInLayer: Boolean, childHitTest: () -> Unit) {
         hitInMinimumTouchTarget(node, -1f, isInLayer, childHitTest)
     }
 
@@ -101,7 +102,7 @@
      * Runs [childHitTest] to do further hit testing for children.
      */
     fun hitInMinimumTouchTarget(
-        node: T,
+        node: Modifier.Node,
         distanceFromEdge: Float,
         isInLayer: Boolean,
         childHitTest: () -> Unit
@@ -124,7 +125,7 @@
      * hit.
      */
     fun speculativeHit(
-        node: T,
+        node: Modifier.Node,
         distanceFromEdge: Float,
         isInLayer: Boolean,
         childHitTest: () -> Unit
@@ -188,9 +189,9 @@
         }
     }
 
-    override fun contains(element: T): Boolean = indexOf(element) != -1
+    override fun contains(element: Modifier.Node): Boolean = indexOf(element) != -1
 
-    override fun containsAll(elements: Collection<T>): Boolean {
+    override fun containsAll(elements: Collection<Modifier.Node>): Boolean {
         elements.forEach {
             if (!contains(it)) {
                 return false
@@ -199,10 +200,9 @@
         return true
     }
 
-    @Suppress("UNCHECKED_CAST")
-    override fun get(index: Int): T = values[index] as T
+    override fun get(index: Int): Modifier.Node = values[index] as Modifier.Node
 
-    override fun indexOf(element: T): Int {
+    override fun indexOf(element: Modifier.Node): Int {
         for (i in 0..lastIndex) {
             if (values[i] == element) {
                 return i
@@ -213,9 +213,9 @@
 
     override fun isEmpty(): Boolean = size == 0
 
-    override fun iterator(): Iterator<T> = HitTestResultIterator()
+    override fun iterator(): Iterator<Modifier.Node> = HitTestResultIterator()
 
-    override fun lastIndexOf(element: T): Int {
+    override fun lastIndexOf(element: Modifier.Node): Int {
         for (i in lastIndex downTo 0) {
             if (values[i] == element) {
                 return i
@@ -224,11 +224,12 @@
         return -1
     }
 
-    override fun listIterator(): ListIterator<T> = HitTestResultIterator()
+    override fun listIterator(): ListIterator<Modifier.Node> = HitTestResultIterator()
 
-    override fun listIterator(index: Int): ListIterator<T> = HitTestResultIterator(index)
+    override fun listIterator(index: Int): ListIterator<Modifier.Node> =
+        HitTestResultIterator(index)
 
-    override fun subList(fromIndex: Int, toIndex: Int): List<T> =
+    override fun subList(fromIndex: Int, toIndex: Int): List<Modifier.Node> =
         SubList(fromIndex, toIndex)
 
     /**
@@ -243,18 +244,18 @@
         var index: Int = 0,
         val minIndex: Int = 0,
         val maxIndex: Int = size
-    ) : ListIterator<T> {
+    ) : ListIterator<Modifier.Node> {
         override fun hasNext(): Boolean = index < maxIndex
 
         override fun hasPrevious(): Boolean = index > minIndex
 
         @Suppress("UNCHECKED_CAST")
-        override fun next(): T = values[index++] as T
+        override fun next(): Modifier.Node = values[index++] as Modifier.Node
 
         override fun nextIndex(): Int = index - minIndex
 
         @Suppress("UNCHECKED_CAST")
-        override fun previous(): T = values[--index] as T
+        override fun previous(): Modifier.Node = values[--index] as Modifier.Node
 
         override fun previousIndex(): Int = index - minIndex - 1
     }
@@ -262,13 +263,13 @@
     private inner class SubList(
         val minIndex: Int,
         val maxIndex: Int
-    ) : List<T> {
+    ) : List<Modifier.Node> {
         override val size: Int
             get() = maxIndex - minIndex
 
-        override fun contains(element: T): Boolean = indexOf(element) != -1
+        override fun contains(element: Modifier.Node): Boolean = indexOf(element) != -1
 
-        override fun containsAll(elements: Collection<T>): Boolean {
+        override fun containsAll(elements: Collection<Modifier.Node>): Boolean {
             elements.forEach {
                 if (!contains(it)) {
                     return false
@@ -277,10 +278,9 @@
             return true
         }
 
-        @Suppress("UNCHECKED_CAST")
-        override fun get(index: Int): T = values[index + minIndex] as T
+        override fun get(index: Int): Modifier.Node = values[index + minIndex] as Modifier.Node
 
-        override fun indexOf(element: T): Int {
+        override fun indexOf(element: Modifier.Node): Int {
             for (i in minIndex..maxIndex) {
                 if (values[i] == element) {
                     return i - minIndex
@@ -291,9 +291,10 @@
 
         override fun isEmpty(): Boolean = size == 0
 
-        override fun iterator(): Iterator<T> = HitTestResultIterator(minIndex, minIndex, maxIndex)
+        override fun iterator(): Iterator<Modifier.Node> =
+            HitTestResultIterator(minIndex, minIndex, maxIndex)
 
-        override fun lastIndexOf(element: T): Int {
+        override fun lastIndexOf(element: Modifier.Node): Int {
             for (i in maxIndex downTo minIndex) {
                 if (values[i] == element) {
                     return i - minIndex
@@ -302,13 +303,13 @@
             return -1
         }
 
-        override fun listIterator(): ListIterator<T> =
+        override fun listIterator(): ListIterator<Modifier.Node> =
             HitTestResultIterator(minIndex, minIndex, maxIndex)
 
-        override fun listIterator(index: Int): ListIterator<T> =
+        override fun listIterator(index: Int): ListIterator<Modifier.Node> =
             HitTestResultIterator(minIndex + index, minIndex, maxIndex)
 
-        override fun subList(fromIndex: Int, toIndex: Int): List<T> =
+        override fun subList(fromIndex: Int, toIndex: Int): List<Modifier.Node> =
             SubList(minIndex + fromIndex, minIndex + toIndex)
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
index 07917f3..9407b95 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
@@ -160,10 +160,10 @@
     }
 
     @OptIn(ExperimentalComposeUiApi::class)
-    override fun <T : DelegatableNode> hitTestChild(
-        hitTestSource: HitTestSource<T>,
+    override fun hitTestChild(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean
     ) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
index c1b1344..b8146be 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
@@ -151,7 +151,7 @@
  * This invalidates the current node's measure result, and ensures that a remeasurement
  * (the measurement block rerun) of this node will happen for the next frame.
  */
-fun LayoutModifierNode.invalidateMeasurements() = requireLayoutNode().invalidateMeasurements()
+fun LayoutModifierNode.invalidateMeasurement() = requireLayoutNode().invalidateMeasurements()
 
 internal fun LayoutModifierNode.requestRemeasure() = requireLayoutNode().requestRemeasure()
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index aff7b04..e42df1f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -22,7 +22,6 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusTargetModifierNode
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.input.pointer.PointerInputFilter
@@ -54,7 +53,6 @@
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.simpleIdentityToString
 import androidx.compose.ui.semantics.generateSemanticsId
-import androidx.compose.ui.semantics.outerSemantics
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.DpSize
@@ -420,8 +418,7 @@
 
         this.owner = owner
         this.depth = (parent?.depth ?: -1) + 1
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (outerSemantics != null) {
+        if (nodes.has(Nodes.Semantics)) {
             owner.onSemanticsChange()
         }
         owner.onAttach(this)
@@ -471,8 +468,7 @@
         layoutDelegate.resetAlignmentLines()
         onDetach?.invoke(owner)
 
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (outerSemantics != null) {
+        if (nodes.has(Nodes.Semantics)) {
             owner.onSemanticsChange()
         }
         nodes.detach()
@@ -888,10 +884,9 @@
      * @param hitTestResult The collection that the hit [PointerInputFilter]s will be
      * added to if hit.
      */
-    @OptIn(ExperimentalComposeUiApi::class)
     internal fun hitTest(
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<PointerInputModifierNode>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean = false,
         isInLayer: Boolean = true
     ) {
@@ -906,10 +901,9 @@
     }
 
     @Suppress("UNUSED_PARAMETER")
-    @OptIn(ExperimentalComposeUiApi::class)
     internal fun hitTestSemantics(
         pointerPosition: Offset,
-        hitSemanticsEntities: HitTestResult<SemanticsModifierNode>,
+        hitSemanticsEntities: HitTestResult,
         isTouchEvent: Boolean = true,
         isInLayer: Boolean = true
     ) {
@@ -1011,18 +1005,11 @@
         }
     }
 
-    @OptIn(ExperimentalComposeUiApi::class)
     private fun invalidateFocusOnDetach() {
-        if (nodes.has(FocusTarget)) {
-            nodes.tailToHead {
-                if (
-                    it.isKind(FocusTarget) &&
-                    it is FocusTargetModifierNode &&
-                    it.focusState.isFocused
-                ) {
-                    requireOwner().focusOwner.clearFocus(force = true, refreshFocusEvents = false)
-                    it.scheduleInvalidationForFocusEvents()
-                }
+        nodes.tailToHead(FocusTarget) {
+            if (it.focusState.isFocused) {
+                requireOwner().focusOwner.clearFocus(force = true, refreshFocusEvents = false)
+                it.scheduleInvalidationForFocusEvents()
             }
         }
     }
@@ -1220,7 +1207,7 @@
     private fun shouldInvalidateParentLayer(): Boolean {
         if (nodes.has(Nodes.Draw) && !nodes.has(Nodes.Layout)) return true
         nodes.headToTail {
-            if (it.isKind(Nodes.Layout) && it is LayoutModifierNode) {
+            if (it.isKind(Nodes.Layout)) {
                 if (it.requireCoordinator(Nodes.Layout).layer != null) {
                     return false
                 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
index 0fa9d4e..23c5790 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
@@ -51,13 +51,15 @@
             // the draw pass as with the new modifier.node / coordinator structure this feels
             // somewhat error prone.
             if (nextDrawNode != null) {
-                nextDrawNode.performDraw(canvas)
+                nextDrawNode.dispatchForKind(Nodes.Draw) {
+                    it.performDraw(canvas)
+                }
             } else {
                 // TODO(lmr): this is needed in the case that the drawnode is also a measure node,
                 //  but we should think about the right ways to handle this as this is very error
                 //  prone i think
                 val coordinator = drawNode.requireCoordinator(Nodes.Draw)
-                val nextCoordinator = if (coordinator.tail === drawNode)
+                val nextCoordinator = if (coordinator.tail === drawNode.node)
                     coordinator.wrapped!!
                 else
                     coordinator
@@ -71,13 +73,24 @@
         val coordinator = requireCoordinator(Nodes.Draw)
         val size = coordinator.size.toSize()
         val drawScope = coordinator.layoutNode.mDrawScope
-        drawScope.draw(canvas, size, coordinator, this)
+        drawScope.drawDirect(canvas, size, coordinator, this)
     }
 
     internal fun draw(
         canvas: Canvas,
         size: Size,
         coordinator: NodeCoordinator,
+        drawNode: Modifier.Node,
+    ) {
+        drawNode.dispatchForKind(Nodes.Draw) {
+            drawDirect(canvas, size, coordinator, it)
+        }
+    }
+
+    internal fun drawDirect(
+        canvas: Canvas,
+        size: Size,
+        coordinator: NodeCoordinator,
         drawNode: DrawModifierNode,
     ) {
         val previousDrawNode = this.drawNode
@@ -97,7 +110,7 @@
 }
 
 @OptIn(ExperimentalComposeUiApi::class)
-private fun DelegatableNode.nextDrawNode(): DrawModifierNode? {
+private fun DelegatableNode.nextDrawNode(): Modifier.Node? {
     val drawMask = Nodes.Draw.mask
     val measureMask = Nodes.Layout.mask
     val child = node.child ?: return null
@@ -106,7 +119,7 @@
     while (next != null) {
         if (next.kindSet and measureMask != 0) return null
         if (next.kindSet and drawMask != 0) {
-            return next as DrawModifierNode
+            return next
         }
         next = next.child
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
index 311cb3c..0bbb386 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
@@ -38,22 +38,6 @@
  */
 abstract class ModifierNodeElement<N : Modifier.Node> : Modifier.Element, InspectableValue {
 
-    /**
-     * If this property returns `true`, then nodes will be automatically invalidated after the
-     * [update] callback completes (For example, if the returned Node is a [DrawModifierNode], its
-     * [DrawModifierNode.invalidateDraw] function will be invoked automatically as part of
-     * auto invalidation).
-     *
-     * This is enabled by default, and provides a convenient mechanism to schedule invalidation
-     * and apply changes made to the modifier. You may choose to set this to `false` if your
-     * modifier has auto-invalidatable properties that do not frequently require invalidation to
-     * improve performance by skipping unnecessary invalidation. If `autoInvalidate` is set to
-     * `false`, you must call the appropriate invalidate functions manually in [update] for the
-     * new attributes to become visible.
-     */
-    open val autoInvalidate: Boolean
-        get() = true
-
     private var _inspectorValues: InspectorInfo? = null
     private val inspectorValues: InspectorInfo
         get() = _inspectorValues ?: InspectorInfo()
@@ -83,7 +67,7 @@
      * application. This function will have the current node instance passed in as a parameter, and
      * it is expected that the node will be brought up to date.
      */
-    abstract fun update(node: N): N
+    abstract fun update(node: N)
 
     /**
      * Populates an [InspectorInfo] object with attributes to display in the layout inspector. This
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
index d99ba77..2b2847f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
@@ -127,7 +127,7 @@
                         // this is "the same" modifier, but some things have changed so we want to
                         // reuse the node but also update it
                         val beforeUpdate = node
-                        node = updateNodeAndReplaceIfNeeded(prev, next, beforeUpdate)
+                        node = updateNode(prev, next, beforeUpdate)
                         logger?.nodeUpdated(i, i, prev, next, beforeUpdate, node)
                     }
                     ActionReuse -> {
@@ -236,19 +236,20 @@
         }
     }
 
-    private fun syncCoordinators() {
+    fun syncCoordinators() {
         var coordinator: NodeCoordinator = innerCoordinator
         var node: Modifier.Node? = tail.parent
         while (node != null) {
-            if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
+            val layoutmod = node.asLayoutModifierNode()
+            if (layoutmod != null) {
                 val next = if (node.coordinator != null) {
                     val c = node.coordinator as LayoutModifierNodeCoordinator
                     val prevNode = c.layoutModifierNode
-                    c.layoutModifierNode = node
+                    c.layoutModifierNode = layoutmod
                     if (prevNode !== node) c.onLayoutModifierNodeChanged()
                     c
                 } else {
-                    val c = LayoutModifierNodeCoordinator(layoutNode, node)
+                    val c = LayoutModifierNodeCoordinator(layoutNode, layoutmod)
                     node.updateCoordinator(c)
                     c
                 }
@@ -394,7 +395,7 @@
             val next = after[newIndex]
             if (prev != next) {
                 val beforeUpdate = node
-                node = updateNodeAndReplaceIfNeeded(prev, next, beforeUpdate)
+                node = updateNode(prev, next, beforeUpdate)
                 logger?.nodeUpdated(oldIndex, newIndex, prev, next, beforeUpdate, node)
             } else {
                 logger?.nodeReused(oldIndex, newIndex, prev, next, node)
@@ -540,7 +541,7 @@
     ): Modifier.Node {
         val node = when (element) {
             is ModifierNodeElement<*> -> element.create().also {
-                it.kindSet = calculateNodeKindSetFrom(it)
+                it.kindSet = calculateNodeKindSetFromIncludingDelegates(it)
             }
             else -> BackwardsCompatNode(element)
         }
@@ -572,37 +573,23 @@
         return node
     }
 
-    private fun updateNodeAndReplaceIfNeeded(
+    private fun updateNode(
         prev: Modifier.Element,
         next: Modifier.Element,
         node: Modifier.Node
     ): Modifier.Node {
         when {
             prev is ModifierNodeElement<*> && next is ModifierNodeElement<*> -> {
-                val updated = next.updateUnsafe(node)
-                if (updated !== node) {
-                    check(!updated.isAttached)
-                    updated.insertedNodeAwaitingAttachForInvalidation = true
-                    // if a new instance is returned, we want to detach the old one
-                    if (node.isAttached) {
-                        autoInvalidateRemovedNode(node)
-                        node.detach()
-                    }
-                    return replaceNode(node, updated)
+                next.updateUnsafe(node)
+                if (node.isAttached) {
+                    // the modifier element is labeled as "auto invalidate", which means
+                    // that since the node was updated, we need to invalidate everything
+                    // relevant to it.
+                    autoInvalidateUpdatedNode(node)
                 } else {
-                    // the node was updated. we are done.
-                    if (next.autoInvalidate) {
-                        if (updated.isAttached) {
-                            // the modifier element is labeled as "auto invalidate", which means
-                            // that since the node was updated, we need to invalidate everything
-                            // relevant to it.
-                            autoInvalidateUpdatedNode(updated)
-                        } else {
-                            updated.updatedNodeAwaitingAttachForInvalidation = true
-                        }
-                    }
-                    return updated
+                    node.updatedNodeAwaitingAttachForInvalidation = true
                 }
+                return node
             }
             node is BackwardsCompatNode -> {
                 node.element = next
@@ -632,7 +619,7 @@
 
     internal inline fun <reified T> headToTail(type: NodeKind<T>, block: (T) -> Unit) {
         headToTail(type.mask) {
-            if (it is T) block(it)
+            it.dispatchForKind(type, block)
         }
     }
 
@@ -671,7 +658,7 @@
 
     internal inline fun <reified T> tailToHead(type: NodeKind<T>, block: (T) -> Unit) {
         tailToHead(type.mask) {
-            if (it is T) block(it)
+            it.dispatchForKind(type, block)
         }
     }
 
@@ -748,9 +735,9 @@
 
 private fun <T : Modifier.Node> ModifierNodeElement<T>.updateUnsafe(
     node: Modifier.Node
-): Modifier.Node {
+) {
     @Suppress("UNCHECKED_CAST")
-    return update(node as T)
+    update(node as T)
 }
 
 private fun Modifier.fillVector(
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 b0430e4..ab86a7a 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
@@ -41,7 +41,7 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.findRootCoordinates
 import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.semantics.outerSemantics
+import androidx.compose.ui.semantics.collapsedSemantics
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
@@ -108,7 +108,7 @@
 
     inline fun <reified T> visitNodes(type: NodeKind<T>, block: (T) -> Unit) {
         visitNodes(type.mask, type.includeSelfInTraversal) {
-            if (it is T) block(it)
+            it.dispatchForKind(type, block)
         }
     }
 
@@ -116,16 +116,8 @@
         return headNode(type.includeSelfInTraversal)?.has(type) == true
     }
 
-    inline fun <reified T> head(type: NodeKind<T>): T? {
-        visitNodes(type.mask, type.includeSelfInTraversal) { return it as? T }
-        return null
-    }
-
-    fun <T> headUnchecked(type: NodeKind<T>): T? {
-        visitNodes(type.mask, type.includeSelfInTraversal) {
-            @Suppress("UNCHECKED_CAST")
-            return it as T
-        }
+    fun head(type: NodeKind<*>): Modifier.Node? {
+        visitNodes(type.mask, type.includeSelfInTraversal) { return it }
         return null
     }
 
@@ -242,8 +234,10 @@
             if (layoutNode.nodes.has(Nodes.ParentData)) {
                 with(layoutNode.density) {
                     layoutNode.nodes.tailToHead {
-                        if (it.isKind(Nodes.ParentData) && it is ParentDataModifierNode) {
-                            data = with(it) { modifyParentData(data) }
+                        if (it.isKind(Nodes.ParentData)) {
+                            it.dispatchForKind(Nodes.ParentData) {
+                                data = with(it) { modifyParentData(data) }
+                            }
                         }
                         if (it === thisNode) return@tailToHead
                     }
@@ -494,14 +488,14 @@
      * This can only be `false` when [isTouchEvent] is `true` or else a layer miss means the event
      * will be clipped out.
      */
-    fun <T : DelegatableNode> hitTest(
-        hitTestSource: HitTestSource<T>,
+    fun hitTest(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean
     ) {
-        val head = headUnchecked(hitTestSource.entityType())
+        val head = head(hitTestSource.entityType())
         if (!withinLayerBounds(pointerPosition)) {
             // This missed the clip, but if this layout is too small and this is within the
             // minimum touch target, we still consider it a hit.
@@ -566,10 +560,10 @@
      * The [NodeCoordinator] had a hit in bounds and can record any children in the
      * [hitTestResult].
      */
-    private fun <T : DelegatableNode> T?.hit(
-        hitTestSource: HitTestSource<T>,
+    private fun Modifier.Node?.hit(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean
     ) {
@@ -577,7 +571,7 @@
             hitTestChild(hitTestSource, pointerPosition, hitTestResult, isTouchEvent, isInLayer)
         } else {
             hitTestResult.hit(this, isInLayer) {
-                nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout)
+                nextUntil(hitTestSource.entityType(), Nodes.Layout)
                     .hit(hitTestSource, pointerPosition, hitTestResult, isTouchEvent, isInLayer)
             }
         }
@@ -587,10 +581,10 @@
      * The [NodeCoordinator] had a hit [distanceFromEdge] from the bounds and it is within
      * the minimum touch target distance, so it should be recorded as such in the [hitTestResult].
      */
-    private fun <T : DelegatableNode> T?.hitNear(
-        hitTestSource: HitTestSource<T>,
+    private fun Modifier.Node?.hitNear(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean,
         distanceFromEdge: Float
@@ -604,7 +598,7 @@
                 distanceFromEdge,
                 isInLayer
             ) {
-                nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).hitNear(
+                nextUntil(hitTestSource.entityType(), Nodes.Layout).hitNear(
                     hitTestSource,
                     pointerPosition,
                     hitTestResult,
@@ -620,10 +614,10 @@
      * The [NodeCoordinator] had a miss, but it hasn't been clipped out. The child must be
      * checked to see if it hit.
      */
-    private fun <T : DelegatableNode> T?.speculativeHit(
-        hitTestSource: HitTestSource<T>,
+    private fun Modifier.Node?.speculativeHit(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean,
         distanceFromEdge: Float
@@ -638,7 +632,7 @@
                 distanceFromEdge,
                 isInLayer
             ) {
-                nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
+                nextUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
                     hitTestSource,
                     pointerPosition,
                     hitTestResult,
@@ -648,7 +642,7 @@
                 )
             }
         } else {
-            nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
+            nextUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
                 hitTestSource,
                 pointerPosition,
                 hitTestResult,
@@ -662,10 +656,10 @@
     /**
      * Do a [hitTest] on the children of this [NodeCoordinator].
      */
-    open fun <T : DelegatableNode> hitTestChild(
-        hitTestSource: HitTestSource<T>,
+    open fun hitTestChild(
+        hitTestSource: HitTestSource,
         pointerPosition: Offset,
-        hitTestResult: HitTestResult<T>,
+        hitTestResult: HitTestResult,
         isTouchEvent: Boolean,
         isInLayer: Boolean
     ) {
@@ -1136,17 +1130,17 @@
      * used in their implementations are different. This extracts the differences between the
      * two methods into a single interface.
      */
-    internal interface HitTestSource<N : DelegatableNode> {
+    internal interface HitTestSource {
         /**
          * Returns the [NodeKind] for the hit test target.
          */
-        fun entityType(): NodeKind<N>
+        fun entityType(): NodeKind<*>
 
         /**
          * Pointer input hit tests can intercept child hits when enabled. This returns `true`
          * if the modifier has requested intercepting.
          */
-        fun interceptOutOfBoundsChildEvents(node: N): Boolean
+        fun interceptOutOfBoundsChildEvents(node: Modifier.Node): Boolean
 
         /**
          * Returns false if the parent layout node has a state that suppresses
@@ -1160,7 +1154,7 @@
         fun childHitTest(
             layoutNode: LayoutNode,
             pointerPosition: Offset,
-            hitTestResult: HitTestResult<N>,
+            hitTestResult: HitTestResult,
             isTouchEvent: Boolean,
             isInLayer: Boolean
         )
@@ -1210,18 +1204,22 @@
          */
         @OptIn(ExperimentalComposeUiApi::class)
         val PointerInputSource =
-            object : HitTestSource<PointerInputModifierNode> {
+            object : HitTestSource {
                 override fun entityType() = Nodes.PointerInput
 
-                override fun interceptOutOfBoundsChildEvents(node: PointerInputModifierNode) =
-                    node.interceptOutOfBoundsChildEvents()
+                override fun interceptOutOfBoundsChildEvents(node: Modifier.Node): Boolean {
+                    node.dispatchForKind(Nodes.PointerInput) {
+                        if (it.interceptOutOfBoundsChildEvents()) return true
+                    }
+                    return false
+                }
 
                 override fun shouldHitTestChildren(parentLayoutNode: LayoutNode) = true
 
                 override fun childHitTest(
                     layoutNode: LayoutNode,
                     pointerPosition: Offset,
-                    hitTestResult: HitTestResult<PointerInputModifierNode>,
+                    hitTestResult: HitTestResult,
                     isTouchEvent: Boolean,
                     isInLayer: Boolean
                 ) = layoutNode.hitTest(pointerPosition, hitTestResult, isTouchEvent, isInLayer)
@@ -1231,19 +1229,18 @@
          * Hit testing specifics for semantics.
          */
         val SemanticsSource =
-            object : HitTestSource<SemanticsModifierNode> {
+            object : HitTestSource {
                 override fun entityType() = Nodes.Semantics
 
-                override fun interceptOutOfBoundsChildEvents(node: SemanticsModifierNode) = false
+                override fun interceptOutOfBoundsChildEvents(node: Modifier.Node) = false
 
                 override fun shouldHitTestChildren(parentLayoutNode: LayoutNode) =
-                    parentLayoutNode.outerSemantics?.collapsedSemanticsConfiguration()
-                        ?.isClearingSemantics != true
+                    parentLayoutNode.collapsedSemantics?.isClearingSemantics != true
 
                 override fun childHitTest(
                     layoutNode: LayoutNode,
                     pointerPosition: Offset,
-                    hitTestResult: HitTestResult<SemanticsModifierNode>,
+                    hitTestResult: HitTestResult,
                     isTouchEvent: Boolean,
                     isInLayer: Boolean
                 ) = layoutNode.hitTestSemantics(
@@ -1308,7 +1305,10 @@
     }
 }
 
-private fun <T> DelegatableNode.nextUncheckedUntil(type: NodeKind<T>, stopType: NodeKind<*>): T? {
+private fun DelegatableNode.nextUntil(
+    type: NodeKind<*>,
+    stopType: NodeKind<*>
+): Modifier.Node? {
     val child = node.child ?: return null
     if (child.aggregateChildKindSet and type.mask == 0) return null
     var next: Modifier.Node? = child
@@ -1316,8 +1316,7 @@
         val kindSet = next.kindSet
         if (kindSet and stopType.mask != 0) return null
         if (kindSet and type.mask != 0) {
-            @Suppress("UNCHECKED_CAST")
-            return next as? T
+            return next
         }
         next = next.child
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
index 6e9b108..b998c618 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
@@ -27,6 +27,9 @@
 import androidx.compose.ui.focus.FocusProperties
 import androidx.compose.ui.focus.FocusPropertiesModifierNode
 import androidx.compose.ui.focus.FocusTargetModifierNode
+import androidx.compose.ui.focus.invalidateFocusEvent
+import androidx.compose.ui.focus.invalidateFocusProperties
+import androidx.compose.ui.focus.invalidateFocusTarget
 import androidx.compose.ui.input.key.KeyInputModifierNode
 import androidx.compose.ui.input.key.SoftKeyboardInterceptionModifierNode
 import androidx.compose.ui.input.pointer.PointerInputModifier
@@ -50,6 +53,8 @@
 
 internal inline infix fun Int.or(other: NodeKind<*>): Int = this or other.mask
 
+internal inline operator fun Int.contains(value: NodeKind<*>): Boolean = this and value.mask != 0
+
 // For a given NodeCoordinator, the "LayoutAware" nodes that it is concerned with should include
 // its own measureNode if the measureNode happens to implement LayoutAware. If the measureNode
 // implements any other node interfaces, such as draw, those should be visited by the coordinator
@@ -144,6 +149,10 @@
 
 @OptIn(ExperimentalComposeUiApi::class)
 internal fun calculateNodeKindSetFrom(node: Modifier.Node): Int {
+    // This function does not take delegates into account, as a result, the kindSet will never
+    // change, so if it is non-zero, it means we've already calculated it and we can just bail
+    // early here.
+    if (node.kindSet != 0) return node.kindSet
     var mask = Nodes.Any.mask
     if (node is LayoutModifierNode) {
         mask = mask or Nodes.Layout
@@ -200,34 +209,62 @@
 private const val Inserted = 1
 private const val Removed = 2
 
-internal fun autoInvalidateRemovedNode(node: Modifier.Node) = autoInvalidateNode(node, Removed)
-
-internal fun autoInvalidateInsertedNode(node: Modifier.Node) = autoInvalidateNode(node, Inserted)
-
-internal fun autoInvalidateUpdatedNode(node: Modifier.Node) = autoInvalidateNode(node, Updated)
-
-private fun autoInvalidateNode(node: Modifier.Node, phase: Int) {
+internal fun autoInvalidateRemovedNode(node: Modifier.Node) {
     check(node.isAttached)
-    if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
-        node.invalidateMeasurements()
+    autoInvalidateNodeIncludingDelegates(node, 0.inv(), Removed)
+}
+
+internal fun autoInvalidateInsertedNode(node: Modifier.Node) {
+    check(node.isAttached)
+    autoInvalidateNodeIncludingDelegates(node, 0.inv(), Inserted)
+}
+
+internal fun autoInvalidateUpdatedNode(node: Modifier.Node) {
+    check(node.isAttached)
+    autoInvalidateNodeIncludingDelegates(node, 0.inv(), Updated)
+}
+
+internal fun autoInvalidateNodeIncludingDelegates(
+    node: Modifier.Node,
+    remainingSet: Int,
+    phase: Int,
+) {
+    if (node is DelegatingNode) {
+        autoInvalidateNodeSelf(node, node.selfKindSet and remainingSet, phase)
+        val newRemaining = remainingSet and node.selfKindSet.inv()
+        node.forEachImmediateDelegate {
+            autoInvalidateNodeIncludingDelegates(it, newRemaining, phase)
+        }
+    } else {
+        autoInvalidateNodeSelf(node, node.kindSet and remainingSet, phase)
+    }
+}
+
+private fun autoInvalidateNodeSelf(node: Modifier.Node, selfKindSet: Int, phase: Int) {
+    // TODO(lmr): Implementing it this way means that delegates of an autoInvalidate=false node will
+    //  still get invalidated. Not sure if that's what we want or not.
+    // Don't invalidate the node if it marks itself as autoInvalidate = false.
+    if (phase == Updated && !node.shouldAutoInvalidate) return
+    if (Nodes.Layout in selfKindSet && node is LayoutModifierNode) {
+        node.invalidateMeasurement()
         if (phase == Removed) {
             val coordinator = node.requireCoordinator(Nodes.Layout)
             coordinator.onRelease()
         }
     }
-    if (node.isKind(Nodes.GlobalPositionAware) && node is GlobalPositionAwareModifierNode) {
+    if (Nodes.GlobalPositionAware in selfKindSet && node is GlobalPositionAwareModifierNode) {
         node.requireLayoutNode().invalidateMeasurements()
     }
-    if (node.isKind(Nodes.Draw) && node is DrawModifierNode) {
+    if (Nodes.Draw in selfKindSet && node is DrawModifierNode) {
         node.invalidateDraw()
     }
-    if (node.isKind(Nodes.Semantics) && node is SemanticsModifierNode) {
+    if (Nodes.Semantics in selfKindSet && node is SemanticsModifierNode) {
         node.invalidateSemantics()
     }
-    if (node.isKind(Nodes.ParentData) && node is ParentDataModifierNode) {
+    if (Nodes.ParentData in selfKindSet && node is ParentDataModifierNode) {
         node.invalidateParentData()
     }
-    if (node.isKind(Nodes.FocusTarget) && node is FocusTargetModifierNode) {
+    if (Nodes.FocusTarget in selfKindSet && node is FocusTargetModifierNode) {
         when (phase) {
             // when we previously had focus target modifier on a node and then this modifier
             // is removed we need to notify the focus tree about so the focus state is reset.
@@ -236,17 +273,17 @@
         }
     }
     if (
-        node.isKind(Nodes.FocusProperties) &&
+        Nodes.FocusProperties in selfKindSet &&
         node is FocusPropertiesModifierNode &&
         node.specifiesCanFocusProperty()
     ) {
         when (phase) {
             Removed -> node.scheduleInvalidationOfAssociatedFocusTargets()
-            else -> node.requireOwner().focusOwner.scheduleInvalidation(node)
+            else -> node.invalidateFocusProperties()
         }
     }
-    if (node.isKind(Nodes.FocusEvent) && node is FocusEventModifierNode && phase != Removed) {
-        node.requireOwner().focusOwner.scheduleInvalidation(node)
+    if (Nodes.FocusEvent in selfKindSet && node is FocusEventModifierNode && phase != Removed) {
+        node.invalidateFocusEvent()
     }
 }
 
@@ -254,7 +291,7 @@
     visitChildren(Nodes.FocusTarget) {
         // Schedule invalidation for the focus target,
         // which will cause it to recalculate focus properties.
-        requireOwner().focusOwner.scheduleInvalidation(it)
+        it.invalidateFocusTarget()
     }
 }
 
@@ -280,4 +317,16 @@
         set(value) { canFocusValue = value }
     fun isCanFocusSet(): Boolean = canFocusValue != null
     fun reset() { canFocusValue = null }
+}
+
+internal fun calculateNodeKindSetFromIncludingDelegates(node: Modifier.Node): Int {
+    return if (node is DelegatingNode) {
+        var mask = node.selfKindSet
+        node.forEachImmediateDelegate {
+            mask = mask or calculateNodeKindSetFromIncludingDelegates(it)
+        }
+        mask
+    } else {
+        calculateNodeKindSetFrom(node)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
index cd01e8b..8c80e15 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
@@ -40,20 +40,12 @@
 
 fun SemanticsModifierNode.invalidateSemantics() = requireOwner().onSemanticsChange()
 
-fun SemanticsModifierNode.collapsedSemanticsConfiguration(): SemanticsConfiguration {
-    val next = localChild(Nodes.Semantics)
-    if (next == null || semanticsConfiguration.isClearingSemantics) {
-        return semanticsConfiguration
-    }
-
-    val config = semanticsConfiguration.copy()
-    config.collapsePeer(next.collapsedSemanticsConfiguration())
-    return config
-}
-
 internal val SemanticsModifierNode.useMinimumTouchTarget: Boolean
     get() = semanticsConfiguration.getOrNull(SemanticsActions.OnClick) != null
 
+internal val SemanticsConfiguration.useMinimumTouchTarget: Boolean
+    get() = getOrNull(SemanticsActions.OnClick) != null
+
 internal fun SemanticsModifierNode.touchBoundsInRoot(): Rect {
     if (!node.isAttached) {
         return Rect.Zero
@@ -64,3 +56,14 @@
 
     return requireCoordinator(Nodes.Semantics).touchBoundsInRoot()
 }
+
+internal fun Modifier.Node.touchBoundsInRoot(useMinimumTouchTarget: Boolean): Rect {
+    if (!node.isAttached) {
+        return Rect.Zero
+    }
+    if (!useMinimumTouchTarget) {
+        return requireCoordinator(Nodes.Semantics).boundsInRoot()
+    }
+
+    return requireCoordinator(Nodes.Semantics).touchBoundsInRoot()
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
index 4c340c7..1477974 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
@@ -57,7 +57,7 @@
 
     override fun create() = CoreSemanticsModifierNode(semanticsConfiguration)
 
-    override fun update(node: CoreSemanticsModifierNode) = node
+    override fun update(node: CoreSemanticsModifierNode) {}
 
     override fun InspectorInfo.inspectableProperties() {
         // Nothing to inspect.
@@ -130,8 +130,8 @@
         return CoreSemanticsModifierNode(semanticsConfiguration)
     }
 
-    override fun update(node: CoreSemanticsModifierNode) = node.apply {
-        semanticsConfiguration = [email protected]
+    override fun update(node: CoreSemanticsModifierNode) {
+        node.semanticsConfiguration = semanticsConfiguration
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -179,8 +179,8 @@
         return CoreSemanticsModifierNode(semanticsConfiguration)
     }
 
-    override fun update(node: CoreSemanticsModifierNode) = node.apply {
-        semanticsConfiguration = this@ClearAndSetSemanticsModifierNodeElement.semanticsConfiguration
+    override fun update(node: CoreSemanticsModifierNode) {
+        node.semanticsConfiguration = semanticsConfiguration
     }
 
     override fun InspectorInfo.inspectableProperties() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index 2ac9f46..41cdc4f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.semantics
 
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -31,28 +30,29 @@
 import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.collapsedSemanticsConfiguration
 import androidx.compose.ui.node.requireCoordinator
 import androidx.compose.ui.node.requireLayoutNode
 import androidx.compose.ui.node.touchBoundsInRoot
+import androidx.compose.ui.node.useMinimumTouchTarget
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 
-/**
- * A list of key/value pairs associated with a layout node or its subtree.
- *
- * Each SemanticsNode takes its id and initial key/value list from the
- * outermost modifier on one layout node.  It also contains the "collapsed" configuration
- * of any other semantics modifiers on the same layout node, and if "mergeDescendants" is
- * specified and enabled, also the "merged" configuration of its subtree.
- */
-@OptIn(ExperimentalComposeUiApi::class)
-class SemanticsNode internal constructor(
+internal fun SemanticsNode(
+    layoutNode: LayoutNode,
+    mergingEnabled: Boolean
+) = SemanticsNode(
+    layoutNode.nodes.head(Nodes.Semantics)!!.node,
+    mergingEnabled,
+    layoutNode,
+    layoutNode.collapsedSemantics!!
+)
+
+internal fun SemanticsNode(
     /*
      * This is expected to be the outermost semantics modifier on a layout node.
      */
-    internal val outerSemanticsNode: SemanticsModifierNode,
+    outerSemanticsNode: SemanticsModifierNode,
     /**
      * mergingEnabled specifies whether mergeDescendants config has any effect.
      *
@@ -63,12 +63,31 @@
      *
      * mergingEnabled is typically true or false consistently on every node of a SemanticsNode tree.
      */
-    val mergingEnabled: Boolean,
-
+    mergingEnabled: Boolean,
     /**
      * The [LayoutNode] that this is associated with.
      */
-    internal val layoutNode: LayoutNode = outerSemanticsNode.requireLayoutNode()
+    layoutNode: LayoutNode = outerSemanticsNode.requireLayoutNode()
+) = SemanticsNode(
+    outerSemanticsNode.node,
+    mergingEnabled,
+    layoutNode,
+    layoutNode.collapsedSemantics ?: SemanticsConfiguration()
+)
+
+/**
+ * A list of key/value pairs associated with a layout node or its subtree.
+ *
+ * Each SemanticsNode takes its id and initial key/value list from the
+ * outermost modifier on one layout node.  It also contains the "collapsed" configuration
+ * of any other semantics modifiers on the same layout node, and if "mergeDescendants" is
+ * specified and enabled, also the "merged" configuration of its subtree.
+ */
+class SemanticsNode internal constructor(
+    internal val outerSemanticsNode: Modifier.Node,
+    val mergingEnabled: Boolean,
+    internal val layoutNode: LayoutNode,
+    internal val unmergedConfig: SemanticsConfiguration,
 ) {
     // We emit fake nodes for several cases. One is to prevent the content description clobbering
     // issue. Another case is  temporary workaround to retrieve default role ordering for Button
@@ -76,12 +95,9 @@
     internal var isFake = false
     private var fakeNodeParent: SemanticsNode? = null
 
-    internal val unmergedConfig = outerSemanticsNode.collapsedSemanticsConfiguration()
-
     internal val isUnmergedLeafNode get() =
         !isFake && replacedChildren.isEmpty() && layoutNode.findClosestParentNode {
-            it.outerSemantics
-                ?.collapsedSemanticsConfiguration()
+            it.collapsedSemantics
                 ?.isMergingSemanticsOfDescendants == true
         } == null
 
@@ -117,7 +133,7 @@
             } else {
                 outerSemanticsNode
             }
-            return entity.touchBoundsInRoot()
+            return entity.node.touchBoundsInRoot(unmergedConfig.useMinimumTouchTarget)
         }
 
     /**
@@ -217,11 +233,7 @@
         if (this.isFake) return listOf()
         val unmergedChildren: MutableList<SemanticsNode> = mutableListOf()
 
-        val semanticsChildren = this.layoutNode.findOneLayerOfSemanticsWrappers()
-
-        semanticsChildren.fastForEach { semanticsChild ->
-            unmergedChildren.add(SemanticsNode(semanticsChild, mergingEnabled))
-        }
+        this.layoutNode.fillOneLayerOfSemanticsWrappers(unmergedChildren)
 
         if (includeFakeNodes) {
             emitFakeNodes(unmergedChildren)
@@ -230,6 +242,20 @@
         return unmergedChildren
     }
 
+    private fun LayoutNode.fillOneLayerOfSemanticsWrappers(
+        list: MutableList<SemanticsNode>
+    ) {
+        // TODO(lmr): visitChildren would be great for this but we would lose the zSorted bit...
+        //  i wonder if we can optimize this for the common case of no z-sortedness going on.
+        zSortedChildren.forEach { child ->
+            if (child.nodes.has(Nodes.Semantics)) {
+                list.add(SemanticsNode(child, mergingEnabled))
+            } else {
+                child.fillOneLayerOfSemanticsWrappers(list)
+            }
+        }
+    }
+
     /**
      * Contains the children in inverse hit test order (i.e. paint order).
      *
@@ -297,21 +323,18 @@
             var node: LayoutNode? = null
             if (mergingEnabled) {
                 node = this.layoutNode.findClosestParentNode {
-                    it.outerSemantics
-                        ?.collapsedSemanticsConfiguration()
-                        ?.isMergingSemanticsOfDescendants == true
+                    it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
                 }
             }
 
             if (node == null) {
-                node = this.layoutNode.findClosestParentNode { it.outerSemantics != null }
+                node = this.layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
             }
 
-            val outerSemantics = node?.outerSemantics
-            if (outerSemantics == null)
+            if (node == null)
                 return null
 
-            return SemanticsNode(outerSemantics, mergingEnabled)
+            return SemanticsNode(node, mergingEnabled)
         }
 
     private fun findOneLayerOfMergingSemanticsNodes(
@@ -337,8 +360,7 @@
      */
     internal fun findCoordinatorToGetBounds(): NodeCoordinator? {
         if (isFake) return parent?.findCoordinatorToGetBounds()
-        val semanticsModifierNode = layoutNode.outerMergingSemantics
-            .takeIf { unmergedConfig.isMergingSemanticsOfDescendants } ?: outerSemanticsNode
+        val semanticsModifierNode = layoutNode.outerMergingSemantics ?: outerSemanticsNode
         return semanticsModifierNode.requireCoordinator(Nodes.Semantics)
     }
 
@@ -373,13 +395,14 @@
         role: Role?,
         properties: SemanticsPropertyReceiver.() -> Unit
     ): SemanticsNode {
+        val configuration = SemanticsConfiguration().also {
+            it.isMergingSemanticsOfDescendants = false
+            it.isClearingSemantics = false
+            it.properties()
+        }
         val fakeNode = SemanticsNode(
             outerSemanticsNode = object : SemanticsModifierNode, Modifier.Node() {
-                override val semanticsConfiguration = SemanticsConfiguration().also {
-                    it.isMergingSemanticsOfDescendants = false
-                    it.isClearingSemantics = false
-                    it.properties()
-                }
+                override val semanticsConfiguration = configuration
             },
             mergingEnabled = false,
             layoutNode = LayoutNode(
@@ -387,43 +410,44 @@
                     semanticsId =
                         if (role != null) roleFakeNodeId() else contentDescriptionFakeNodeId()
                 ),
+            unmergedConfig = configuration
         )
         fakeNode.isFake = true
         fakeNode.fakeNodeParent = this
         return fakeNode
     }
+
+    internal fun copyWithMergingEnabled(): SemanticsNode {
+        return SemanticsNode(
+            outerSemanticsNode,
+            true,
+            layoutNode,
+            unmergedConfig
+        )
+    }
 }
 
-/**
- * Returns the outermost semantics node on a LayoutNode.
- */
-@OptIn(ExperimentalComposeUiApi::class)
-internal val LayoutNode.outerSemantics: SemanticsModifierNode?
-    get() = nodes.head(Nodes.Semantics)
+internal val LayoutNode.collapsedSemantics: SemanticsConfiguration?
+    get() {
+        var result: SemanticsConfiguration? = null
+        nodes.tailToHead(Nodes.Semantics) {
+            val current = result
+            if (current == null || it.semanticsConfiguration.isClearingSemantics) {
+                result = it.semanticsConfiguration
+            } else {
+                result = it.semanticsConfiguration.copy().also {
+                    it.collapsePeer(current)
+                }
+            }
+        }
+        return result
+    }
 
-@OptIn(ExperimentalComposeUiApi::class)
 internal val LayoutNode.outerMergingSemantics: SemanticsModifierNode?
     get() = nodes.firstFromHead(Nodes.Semantics) {
         it.semanticsConfiguration.isMergingSemanticsOfDescendants
     }
 
-@OptIn(ExperimentalComposeUiApi::class)
-private fun LayoutNode.findOneLayerOfSemanticsWrappers(
-    list: MutableList<SemanticsModifierNode> = mutableListOf()
-): List<SemanticsModifierNode> {
-    // TODO(lmr): visitChildren would be great for this but we would lose the zSorted bit...
-    //  i wonder if we can optimize this for the common case of no z-sortedness going on.
-    zSortedChildren.forEach { child ->
-        val outerSemantics = child.outerSemantics
-        if (outerSemantics != null) {
-            list.add(outerSemantics)
-        } else {
-            child.findOneLayerOfSemanticsWrappers(list)
-        }
-    }
-    return list
-}
-
 /**
  * Executes [selector] on every parent of this [LayoutNode] and returns the closest
  * [LayoutNode] to return `true` from [selector] or null if [selector] returns false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
index 7e32c0b..a5d43a5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
@@ -32,12 +32,12 @@
      */
     val rootSemanticsNode: SemanticsNode
         get() {
-            return SemanticsNode(rootNode.outerSemantics!!, mergingEnabled = true)
+            return SemanticsNode(rootNode, mergingEnabled = true)
         }
 
     val unmergedRootSemanticsNode: SemanticsNode
         get() {
-            return SemanticsNode(rootNode.outerSemantics!!, mergingEnabled = false)
+            return SemanticsNode(rootNode, mergingEnabled = false)
         }
 }
 
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
new file mode 100644
index 0000000..2fc9795
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
@@ -0,0 +1,888 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.node
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.unit.Constraints
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import com.google.common.truth.Truth.assertThat
+
+@RunWith(JUnit4::class)
+class DelegatingNodeTest {
+
+    @Test
+    fun testKindSetIncludesDelegates() {
+        assertThat(DelegatedWrapper { DrawMod() }.kindSet)
+            .isEqualTo(Nodes.Any or Nodes.Draw)
+    }
+
+    @Test
+    fun testKindSetUpdatesAfter() {
+        val a = DrawMod("a")
+        val b = DelegatedWrapper { LayoutMod("b") }
+        val c = object : DelegatingNode() {}
+        val chain = layout(a, b, c)
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Layout))
+        assert(!chain.has(Nodes.Semantics))
+
+        assert(a.kindSet == Nodes.Any or Nodes.Draw)
+        assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Layout)
+
+        assert(b.kindSet == Nodes.Any or Nodes.Layout)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
+
+        assert(c.kindSet == Nodes.Any.mask)
+        assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+        c.delegateUnprotected(SemanticsMod("c"))
+        assert(chain.has(Nodes.Semantics))
+
+        assert(a.kindSet == Nodes.Any or Nodes.Draw)
+        assert(a.aggregateChildKindSet ==
+            Nodes.Any or Nodes.Draw or Nodes.Layout or Nodes.Semantics)
+
+        assert(b.kindSet == Nodes.Any or Nodes.Layout)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout or Nodes.Semantics)
+
+        assert(c.kindSet == Nodes.Any or Nodes.Semantics)
+        assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
+    }
+
+    @Test
+    fun testAsKindReturnsDelegate() {
+        val node = DelegatedWrapper { DrawMod() }
+        assert(node.isKind(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) is DrawMod)
+        assert(node.asKind(Nodes.Draw) === node.wrapped)
+    }
+
+    @Test
+    fun testAsKindReturnsNestedDelegate() {
+        val node = DelegatedWrapper { DelegatedWrapper { DrawMod() } }
+        assert(node.isKind(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) is DrawMod)
+        assert(node.asKind(Nodes.Draw) === node.wrapped.wrapped)
+    }
+
+    @Test
+    fun testAsKindReturnsSelf() {
+        val node = object : DrawModifierNode, DelegatingNode() {
+            val wrapped = delegate(DrawMod())
+            override fun ContentDrawScope.draw() {
+                with(wrapped) { draw() }
+            }
+        }
+        assert(node.isKind(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) is DrawModifierNode)
+        assert(node.asKind(Nodes.Draw) === node)
+    }
+
+    @Test
+    fun testAsKindMultipleDelegatesReturnsLast() {
+        val node = object : DelegatingNode() {
+            val first = delegate(DrawMod())
+            val second = delegate(DrawMod())
+        }
+        assert(node.isKind(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) is DrawModifierNode)
+        assert(node.asKind(Nodes.Draw) === node.second)
+    }
+
+    @Test
+    fun testDispatchForMultipleDelegatesSameKind() {
+        val node = object : DelegatingNode() {
+            val first = delegate(DelegatedWrapper { DrawMod("first") })
+            val second = delegate(DrawMod("second"))
+        }
+        assertDispatchOrder(node, Nodes.Draw, node.first.wrapped, node.second)
+    }
+
+    @Test
+    fun testDispatchForSelfOnlyDispatchesToSelf() {
+        val node = object : DrawModifierNode, DelegatingNode() {
+            val wrapped = delegate(DrawMod())
+            override fun ContentDrawScope.draw() {
+                with(wrapped) { draw() }
+            }
+        }
+        assertDispatchOrder(node, Nodes.Draw, node)
+    }
+
+    @Test
+    fun testDispatchNestedSelfStops() {
+        val node = object : DelegatingNode() {
+            val first = delegate(DrawMod())
+            val second = delegate(DrawMod())
+            val third = delegate(object : DrawModifierNode, DelegatingNode() {
+                val first = delegate(DrawMod())
+                val second = delegate(DrawMod())
+                override fun ContentDrawScope.draw() {
+                    with(first) { draw() }
+                }
+            })
+        }
+        assertDispatchOrder(node, Nodes.Draw,
+            node.first,
+            node.second,
+            node.third
+        )
+    }
+
+    @Test
+    fun testHeadToTailNoDelegates() {
+        val a = DrawMod("a")
+        val b = DrawMod("b")
+        val chain = layout(a, b)
+        val recorder = Recorder()
+        chain.headToTail(Nodes.Draw, recorder)
+        assertThat(recorder.recorded).isEqualTo(listOf(a, b))
+    }
+
+    @Test
+    fun testHeadToTailWithDelegate() {
+        val a = DelegatedWrapper { DrawMod() }
+        val b = DrawMod()
+        val chain = layout(a, b)
+        val recorder = Recorder()
+        chain.headToTail(Nodes.Draw, recorder)
+        assertThat(recorder.recorded).isEqualTo(listOf(a.wrapped, b))
+    }
+
+    @Test
+    fun testVisitSubtreeWithDelegates() {
+        val x = DrawMod("x")
+        val a = DelegatedWrapper { DrawMod("a") }
+        val b = DrawMod("b")
+        val c = DelegatedWrapper { DrawMod("c") }
+        val d = DrawMod("d")
+        layout(x, a, b) {
+            layout(c)
+            layout(d)
+        }
+        val recorder = Recorder()
+        x.visitSubtree(Nodes.Draw, recorder)
+        assertThat(recorder.recorded)
+            .isEqualTo(listOf(
+                a.wrapped,
+                b,
+                d,
+                c.wrapped,
+            ))
+    }
+
+    @Test
+    fun testVisitAncestorsWithDelegates() {
+        val x = DrawMod("x")
+        val a = DelegatedWrapper { DrawMod("a") }
+        val b = DrawMod("b")
+        val c = DelegatedWrapper { DrawMod("c") }
+        val d = DrawMod("d")
+        layout(a) {
+            layout(b) {
+                layout(c) {
+                    layout(d, x)
+                }
+            }
+        }
+        val recorder = Recorder()
+        x.visitAncestors(Nodes.Draw, block = recorder)
+        assertThat(recorder.recorded)
+            .isEqualTo(listOf(
+                d,
+                c.wrapped,
+                b,
+                a.wrapped,
+            ))
+    }
+
+    @Test
+    fun testUndelegate() {
+        val node = object : DelegatingNode() {}
+        val chain = layout(node)
+
+        assert(!node.isKind(Nodes.Draw))
+        assert(!chain.has(Nodes.Draw))
+
+        val draw = node.delegateUnprotected(DrawMod())
+
+        assert(node.isKind(Nodes.Draw))
+        assert(chain.has(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) === draw)
+
+        node.undelegateUnprotected(draw)
+
+        assert(!draw.isAttached)
+        assert(node.isAttached)
+
+        assert(!node.isKind(Nodes.Draw))
+        assert(!chain.has(Nodes.Draw))
+    }
+
+    @Test
+    fun testUndelegateWithMultipleDelegates() {
+        val node = object : DelegatingNode() {}
+        val chain = layout(node)
+
+        assert(!node.isKind(Nodes.Draw))
+        assert(!chain.has(Nodes.Draw))
+
+        val draw = node.delegateUnprotected(DrawMod())
+        val layout = node.delegateUnprotected(LayoutMod())
+        val semantics = node.delegateUnprotected(SemanticsMod())
+        val draw2 = node.delegateUnprotected(DrawMod())
+
+        assert(node.isKind(Nodes.Semantics))
+        assert(chain.has(Nodes.Semantics))
+        assert(node.asKind(Nodes.Semantics) === semantics)
+
+        assert(node.isKind(Nodes.Draw))
+        assert(chain.has(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) === draw2)
+
+        assert(node.isKind(Nodes.Layout))
+        assert(chain.has(Nodes.Layout))
+        assert(node.asKind(Nodes.Layout) === layout)
+
+        node.undelegateUnprotected(semantics)
+
+        assert(!node.isKind(Nodes.Semantics))
+        assert(!chain.has(Nodes.Semantics))
+
+        assert(node.isKind(Nodes.Draw))
+        assert(chain.has(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) === draw2)
+
+        assert(node.isKind(Nodes.Layout))
+        assert(chain.has(Nodes.Layout))
+        assert(node.asKind(Nodes.Layout) === layout)
+
+        node.undelegateUnprotected(draw2)
+
+        assert(node.isKind(Nodes.Draw))
+        assert(chain.has(Nodes.Draw))
+        assert(node.asKind(Nodes.Draw) === draw)
+
+        assert(node.isKind(Nodes.Layout))
+        assert(chain.has(Nodes.Layout))
+        assert(node.asKind(Nodes.Layout) === layout)
+    }
+
+    @Test
+    fun testDelegateUndelegateInChain() {
+        val a = object : DelegatingNode() {}
+        val b = object : DelegatingNode() {}
+        val c = object : DelegatingNode() {}
+        val chain = layout(a, b, c)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+
+        val draw = c.delegateUnprotected(DrawMod())
+        assert(chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any.mask)
+        assert(b.kindSet == Nodes.Any.mask)
+        assert(c.kindSet == Nodes.Any or Nodes.Draw)
+        assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+        assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        val sem = b.delegateUnprotected(SemanticsMod())
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any.mask)
+        assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+        assert(c.kindSet == Nodes.Any or Nodes.Draw)
+        assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        val lm = a.delegateUnprotected(LayoutMod())
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any or Nodes.Layout)
+        assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+        assert(c.kindSet == Nodes.Any or Nodes.Draw)
+        assert(a.aggregateChildKindSet ==
+            Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        c.undelegateUnprotected(draw)
+        assert(!chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any or Nodes.Layout)
+        assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+        assert(c.kindSet == Nodes.Any.mask)
+        assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+        assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
+        assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+        b.undelegateUnprotected(sem)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any or Nodes.Layout)
+        assert(b.kindSet == Nodes.Any.mask)
+        assert(c.kindSet == Nodes.Any.mask)
+        assert(a.aggregateChildKindSet == Nodes.Any.mask or Nodes.Layout)
+        assert(b.aggregateChildKindSet == Nodes.Any.mask)
+        assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+        a.undelegateUnprotected(lm)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(a.kindSet == Nodes.Any.mask)
+        assert(b.kindSet == Nodes.Any.mask)
+        assert(c.kindSet == Nodes.Any.mask)
+        assert(a.aggregateChildKindSet == Nodes.Any.mask)
+        assert(b.aggregateChildKindSet == Nodes.Any.mask)
+        assert(c.aggregateChildKindSet == Nodes.Any.mask)
+    }
+
+    @Test
+    fun testDelegateUndelegateInNode() {
+        val node = object : DelegatingNode() {}
+        val chain = layout(node)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+
+        val draw = node.delegateUnprotected(DrawMod())
+        assert(chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        val sem = node.delegateUnprotected(SemanticsMod())
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+        val lm = node.delegateUnprotected(LayoutMod())
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+        assert(node.aggregateChildKindSet ==
+            Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+
+        node.undelegateUnprotected(draw)
+        assert(!chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+
+        node.undelegateUnprotected(sem)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Layout)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
+
+        node.undelegateUnprotected(lm)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any.mask)
+        assert(node.aggregateChildKindSet == Nodes.Any.mask)
+    }
+
+    @Test
+    fun testDelegateUndelegateNested() {
+        val node = object : DelegatingNode() {}
+        val chain = layout(node)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+
+        val draw = node.delegateUnprotected(DelegatedWrapper { DrawMod() })
+        assert(chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        val sem = draw.delegateUnprotected(DelegatedWrapper { SemanticsMod() })
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+        val lm = sem.delegateUnprotected(LayoutMod())
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+        assert(node.aggregateChildKindSet ==
+            Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+
+        sem.undelegateUnprotected(lm)
+        assert(chain.has(Nodes.Draw))
+        assert(chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+        draw.undelegateUnprotected(sem)
+        assert(chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any or Nodes.Draw)
+        assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+        node.undelegateUnprotected(draw)
+        assert(!chain.has(Nodes.Draw))
+        assert(!chain.has(Nodes.Semantics))
+        assert(!chain.has(Nodes.Layout))
+        assert(node.kindSet == Nodes.Any.mask)
+        assert(node.aggregateChildKindSet == Nodes.Any.mask)
+    }
+
+    @Test
+    fun testUndelegateForNestedDelegate() {
+        val a = object : DelegatingNode() {}
+        val chain = layout(a)
+
+        val b = a.delegateUnprotected(object : DelegatingNode() {})
+        val c = a.delegateUnprotected(object : DelegatingNode() {})
+
+        assert(!chain.has(Nodes.Draw))
+        assert(!a.isKind(Nodes.Draw))
+
+        val draw = c.delegateUnprotected(DrawMod())
+
+        assert(chain.has(Nodes.Draw))
+        assert(a.isKind(Nodes.Draw))
+        assert(!b.isKind(Nodes.Draw))
+        assert(c.isKind(Nodes.Draw))
+
+        c.undelegateUnprotected(draw)
+
+        assert(!chain.has(Nodes.Draw))
+        assert(!a.isKind(Nodes.Draw))
+        assert(!b.isKind(Nodes.Draw))
+        assert(!c.isKind(Nodes.Draw))
+    }
+
+    @Test
+    fun testInvalidateInsertedNode() {
+        val node = object : DelegatingNode() {
+            val draw = delegate(DrawMod())
+            val layout = delegate(LayoutMod())
+            val semantics = delegate(SemanticsMod())
+        }
+        val chain = layout(node)
+        chain.clearInvalidations()
+
+        autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
+
+        assert(chain.drawInvalidated())
+        assert(chain.layoutInvalidated())
+        assert(chain.semanticsInvalidated())
+    }
+
+    @Test
+    fun testNestedNodeInvalidation() {
+        val node = object : DelegatingNode() {
+            val wrapped = delegate(
+                DelegatedWrapper {
+                    DelegatedWrapper { DrawMod() }
+                }
+            )
+        }
+        val chain = layout(node)
+        chain.clearInvalidations()
+
+        autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
+
+        assert(chain.drawInvalidated())
+        assert(!chain.layoutInvalidated())
+        assert(!chain.semanticsInvalidated())
+    }
+
+    @Test
+    fun testDelegateUndelegateCausesInvalidationsForDelegateKindsOnly() {
+        val node = object : DelegatingNode() {
+            val semantics = delegate(SemanticsMod())
+        }
+        val chain = layout(node)
+        chain.clearInvalidations()
+
+        val draw = node.delegateUnprotected(DrawMod())
+        assert(chain.drawInvalidated())
+        assert(!chain.semanticsInvalidated())
+
+        chain.clearInvalidations()
+        node.undelegateUnprotected(draw)
+
+        assert(chain.drawInvalidated())
+        assert(!chain.semanticsInvalidated())
+    }
+
+    @Test
+    fun testDelegatingToLayoutNodeUpdatesCoordinators() {
+        val a = DrawMod()
+        val b = object : DelegatingNode() {}
+        val c = LayoutMod()
+        layout(a, b, c)
+
+        val aCoord = a.requireCoordinator(Nodes.Any)
+        val bCoord = b.requireCoordinator(Nodes.Any)
+        val cCoord = c.requireCoordinator(Nodes.Any)
+
+        assert(cCoord === bCoord)
+        assert(cCoord === aCoord)
+
+        val lm = b.delegateUnprotected(LayoutMod())
+
+        assert(cCoord === c.requireCoordinator(Nodes.Any))
+        assert(cCoord !== b.requireCoordinator(Nodes.Any))
+        assert(cCoord !== a.requireCoordinator(Nodes.Any))
+
+        assert(a.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
+
+        b.undelegateUnprotected(lm)
+
+        assert(c.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
+        assert(c.requireCoordinator(Nodes.Any) === a.requireCoordinator(Nodes.Any))
+    }
+
+    @Test
+    fun testDelegateAttachDetach() {
+        val a = object : DelegatingNode() {}
+        val b = object : DelegatingNode() {}
+        val c = DrawMod()
+        a.delegateUnprotected(b)
+        b.delegateUnprotected(c)
+
+        // not attached yet, but the nodes should all point to a
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+        assert(!c.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+        assert(c.node === a)
+
+        val chain = layout(a)
+
+        // attached now, nodes should still point to a
+        assert(a.isAttached)
+        assert(b.isAttached)
+        assert(c.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+        assert(c.node === a)
+
+        // detached now, nodes should still point to a
+        chain.detach()
+
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+        assert(!c.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+        assert(c.node === a)
+
+        chain.attach()
+
+        // attached now, nodes should still point to a
+        assert(a.isAttached)
+        assert(b.isAttached)
+        assert(c.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+        assert(c.node === a)
+
+        b.undelegateUnprotected(c)
+        a.undelegateUnprotected(b)
+
+        // delegates are detached. nodes should point to themselves
+        assert(a.isAttached)
+        assert(!b.isAttached)
+        assert(!c.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === b)
+        assert(c.node === c)
+    }
+
+    @Test
+    fun testDelegateInAttachUndelegateInDetach() {
+        val b = DrawMod()
+        val a = object : DelegatingNode() {
+            override fun onAttach() {
+                delegate(b)
+            }
+
+            override fun onDetach() {
+                undelegate(b)
+            }
+        }
+
+        // not attached yet or delegated yet
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === b)
+
+        val chain = layout(a)
+
+        // attached now, nodes should now point to a
+        assert(a.isAttached)
+        assert(b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+
+        chain.detach()
+
+        // detached AND undelegated now
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === b)
+
+        chain.attach()
+
+        // attached and delegated now
+        assert(a.isAttached)
+        assert(b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+    }
+
+    @Test
+    fun testDelegateInAttach() {
+        val b = DrawMod()
+        val a = object : DelegatingNode() {
+            override fun onAttach() {
+                delegate(b)
+            }
+        }
+
+        // not attached yet or delegated yet
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === b)
+
+        val chain = layout(a)
+
+        // attached now, nodes should now point to a
+        assert(a.isAttached)
+        assert(b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+
+        chain.detach()
+
+        // detached now, still delegated
+        assert(!a.isAttached)
+        assert(!b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+
+        chain.attach()
+
+        // attached, still delegated
+        assert(a.isAttached)
+        assert(b.isAttached)
+
+        assert(a.node === a)
+        assert(b.node === a)
+    }
+}
+
+private fun NodeChain.clearInvalidations() {
+    val owner = layoutNode.owner
+    check(owner is MockOwner)
+    owner.onRequestMeasureParams.clear()
+    owner.invalidatedLayers.clear()
+    owner.semanticsChanged = false
+}
+
+private fun NodeChain.layoutInvalidated(): Boolean {
+    val owner = layoutNode.owner
+    check(owner is MockOwner)
+    return owner.onRequestMeasureParams.isNotEmpty()
+}
+
+private fun NodeChain.drawInvalidated(): Boolean {
+    val owner = layoutNode.owner
+    check(owner is MockOwner)
+    return owner.invalidatedLayers.isNotEmpty()
+}
+
+private fun NodeChain.semanticsInvalidated(): Boolean {
+    val owner = layoutNode.owner
+    check(owner is MockOwner)
+    return owner.semanticsChanged
+}
+
+internal fun layout(
+    vararg modifiers: Modifier.Node,
+    block: LayoutScope.() -> Unit = {}
+): NodeChain {
+    val owner = MockOwner()
+    val root = LayoutNode()
+    val ln = unattachedLayout(*modifiers)
+    LayoutScopeImpl(ln).block()
+    root.insertAt(0, ln)
+    root.attach(owner)
+    root.innerCoordinator.updateLayerBlock({})
+    return ln.nodes
+}
+
+private fun unattachedLayout(vararg modifiers: Modifier.Node): LayoutNode {
+    val ln = LayoutNode()
+    var m: Modifier = Modifier
+    for (node in modifiers) {
+        m = m.then(NodeElement(node))
+    }
+    ln.nodes.updateFrom(m)
+    return ln
+}
+
+internal data class NodeElement(val node: Modifier.Node) : ModifierNodeElement<Modifier.Node>() {
+    override fun create(): Modifier.Node = node
+    override fun update(node: Modifier.Node) {}
+}
+
+class Recorder : (Any) -> Unit {
+    val recorded = mutableListOf<Any>()
+    override fun invoke(p1: Any) {
+        recorded.add(p1)
+    }
+}
+internal class LayoutScopeImpl(val layout: LayoutNode) : LayoutScope {
+    override fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit) {
+        val ln = unattachedLayout(*modifiers)
+        LayoutScopeImpl(ln).block()
+        layout.insertAt(layout.children.size, ln)
+    }
+}
+interface LayoutScope {
+    fun layout(vararg modifiers: Modifier.Node) = layout(*modifiers) {}
+    fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit)
+}
+
+internal inline fun <reified T> assertDispatchOrder(
+    node: Modifier.Node,
+    kind: NodeKind<T>,
+    vararg expected: T
+) {
+    val dispatches = mutableListOf<T>()
+    node.dispatchForKind(kind) {
+        dispatches.add(it)
+    }
+    assertThat(dispatches.toTypedArray()).isEqualTo(expected)
+}
+
+class DrawMod(val id: String = "") : DrawModifierNode, Modifier.Node() {
+    override fun ContentDrawScope.draw() {}
+    override fun toString(): String {
+        return "DrawMod($id)"
+    }
+}
+
+class SemanticsMod(val id: String = "") : SemanticsModifierNode, Modifier.Node() {
+    override val semanticsConfiguration: SemanticsConfiguration
+        get() = SemanticsConfiguration()
+    override fun toString(): String {
+        return "SemanticsMod($id)"
+    }
+}
+
+class LayoutMod(val id: String = "") : LayoutModifierNode, Modifier.Node() {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val placeable = measurable.measure(constraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+    override fun toString(): String {
+        return "LayoutMod($id)"
+    }
+}
+
+class DelegatedWrapper<T : Modifier.Node>(fn: () -> T) : DelegatingNode() {
+    val wrapped = delegate(fn())
+    override fun toString(): String = "Wrapped<$wrapped>"
+}
+
+internal inline fun <reified T> Modifier.Node.asKind(kind: NodeKind<T>): T? {
+    if (!isKind(kind)) return null
+    if (this is T) return this
+    if (this is DelegatingNode) {
+        forEachDelegateBreadthFirst {
+            if (it is T) return it
+        }
+    }
+    return null
+}
+
+internal inline fun DelegatingNode.forEachDelegateBreadthFirst(block: (Modifier.Node) -> Unit) {
+    var node: Modifier.Node? = delegate
+    var queue: ArrayDeque<Modifier.Node>? = null
+    while (node != null) {
+        block(node)
+        if (node is DelegatingNode) {
+            queue = queue.enqueue(node.delegate)
+        }
+        node = node.child ?: queue?.removeFirst()
+    }
+}
+
+private fun ArrayDeque<Modifier.Node>?.enqueue(node: Modifier.Node?): ArrayDeque<Modifier.Node>? {
+    if (node == null) return this
+    val queue = this ?: ArrayDeque(8)
+    queue.addLast(node)
+    return queue
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
index 711c503..665bb01 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.node
 
+import androidx.compose.ui.Modifier
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -26,7 +27,7 @@
 class HitTestResultTest {
     @Test
     fun testHit() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hit("Hello", true) {
             hitTestResult.hit("World", true) {
                 assertThat(hitTestResult.hasHit()).isFalse()
@@ -38,18 +39,18 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(0f, false)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
 
         hitTestResult.hit("Baz", true) {}
         assertThat(hitTestResult.hasHit()).isTrue()
         assertThat(hitTestResult).hasSize(1)
-        assertThat(hitTestResult[0]).isEqualTo("Baz")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
     }
 
     @Test
     fun testHitClipped() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hit("Hello", false) {
             hitTestResult.hit("World", false) {
                 assertThat(hitTestResult.hasHit()).isFalse()
@@ -61,18 +62,18 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(0f, false)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
 
         hitTestResult.hit("Baz", false) {}
         assertThat(hitTestResult.hasHit()).isFalse()
         assertThat(hitTestResult).hasSize(1)
-        assertThat(hitTestResult[0]).isEqualTo("Baz")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
     }
 
     @Test
     fun testHitInMinimumTouchTarget() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hitInMinimumTouchTarget("Hello", 1f, true) {
             hitTestResult.hitInMinimumTouchTarget("World", 2f, false) { }
             assertThat(hitTestResult.hasHit()).isFalse()
@@ -85,18 +86,18 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.5f, true)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
 
         hitTestResult.hitInMinimumTouchTarget("Baz", 0.5f, false) { }
         assertThat(hitTestResult.hasHit()).isFalse()
         assertThat(hitTestResult).hasSize(1)
-        assertThat(hitTestResult[0]).isEqualTo("Baz")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
     }
 
     @Test
     fun testHasHit() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hitInMinimumTouchTarget("Hello", 1f, true) {
             hitTestResult.hit("World", true) {
                 assertThat(hitTestResult.hasHit()).isFalse()
@@ -108,7 +109,7 @@
 
     @Test
     fun testEasySpeculativeHit() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.speculativeHit("Hello", 1f, true) {
         }
         assertThat(hitTestResult).hasSize(0)
@@ -122,20 +123,20 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.5f, true)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
     }
 
     @Test
     fun testSpeculativeHitWithMove() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hitInMinimumTouchTarget("Foo", 1.5f, true) { }
 
         hitTestResult.speculativeHit("Hello", 1f, true) {
         }
 
         assertThat(hitTestResult).hasSize(1)
-        assertThat(hitTestResult[0]).isEqualTo("Foo")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Foo"))
 
         hitTestResult.speculativeHit("Hello", 1f, true) {
             hitTestResult.hitInMinimumTouchTarget("World", 2f, true) {}
@@ -146,13 +147,13 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.25f, true)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
     }
 
     @Test
     fun testSpeculateHitWithDeepHit() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
         hitTestResult.hitInMinimumTouchTarget("Foo", 1.5f, true) { }
 
         hitTestResult.speculativeHit("Hello", 2f, true) {
@@ -164,8 +165,8 @@
         assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.25f, true)).isFalse()
 
         assertThat(hitTestResult).hasSize(2)
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
 
         hitTestResult.speculativeHit("Goodbye", 2f, true) {
             hitTestResult.hitInMinimumTouchTarget("Cruel", 1f, true) {
@@ -173,7 +174,7 @@
             }
         }
 
-        assertThat(hitTestResult.toList()).isEqualTo(listOf("Goodbye", "Cruel", "World!"))
+        assertThat(hitTestResult.toList()).isEqualTo(nodeListOf("Goodbye", "Cruel", "World!"))
     }
 
     @Test
@@ -198,18 +199,18 @@
     @Test
     fun testContainsAll() {
         val hitTestResult = fillHitTestResult()
-        assertThat(hitTestResult.containsAll(listOf("Hello", "great", "this"))).isTrue()
-        assertThat(hitTestResult.containsAll(listOf("Hello", "great", "foo", "this"))).isFalse()
+        assertThat(hitTestResult.containsAll(nodeListOf("Hello", "great", "this"))).isTrue()
+        assertThat(hitTestResult.containsAll(nodeListOf("Hello", "great", "foo", "this"))).isFalse()
     }
 
     @Test
     fun testGet() {
         val hitTestResult = fillHitTestResult()
-        assertThat(hitTestResult[0]).isEqualTo("Hello")
-        assertThat(hitTestResult[1]).isEqualTo("World")
-        assertThat(hitTestResult[2]).isEqualTo("this")
-        assertThat(hitTestResult[3]).isEqualTo("is")
-        assertThat(hitTestResult[4]).isEqualTo("great")
+        assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+        assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
+        assertThat(hitTestResult[2]).isEqualTo(SNode("this"))
+        assertThat(hitTestResult[3]).isEqualTo(SNode("is"))
+        assertThat(hitTestResult[4]).isEqualTo(SNode("great"))
     }
 
     @Test
@@ -229,14 +230,14 @@
         assertThat(hitTestResult.isEmpty()).isFalse()
         hitTestResult.clear()
         assertThat(hitTestResult.isEmpty()).isTrue()
-        assertThat(HitTestResult<String>().isEmpty()).isTrue()
+        assertThat(HitTestResult().isEmpty()).isTrue()
     }
 
     @Test
     fun testIterator() {
         val hitTestResult = fillHitTestResult()
         assertThat(hitTestResult.toList()).isEqualTo(
-            listOf("Hello", "World", "this", "is", "great")
+            nodeListOf("Hello", "World", "this", "is", "great")
         )
     }
 
@@ -266,12 +267,12 @@
             assertThat(iterator.hasNext()).isTrue()
             val hasPrevious = (index != 0)
             assertThat(iterator.hasPrevious()).isEqualTo(hasPrevious)
-            assertThat(iterator.next()).isEqualTo(value)
+            assertThat(iterator.next()).isEqualTo(SNode(value))
         }
 
         for (index in values.lastIndex downTo 0) {
             val value = values[index]
-            assertThat(iterator.previous()).isEqualTo(value)
+            assertThat(iterator.previous()).isEqualTo(SNode(value))
         }
     }
 
@@ -290,12 +291,12 @@
             assertThat(iterator.hasNext()).isTrue()
             val hasPrevious = (index != 0)
             assertThat(iterator.hasPrevious()).isEqualTo(hasPrevious)
-            assertThat(iterator.next()).isEqualTo(values[index])
+            assertThat(iterator.next()).isEqualTo(SNode(values[index]))
         }
 
         for (index in values.lastIndex downTo 0) {
             val value = values[index]
-            assertThat(iterator.previous()).isEqualTo(value)
+            assertThat(iterator.previous()).isEqualTo(SNode(value))
         }
     }
 
@@ -305,45 +306,45 @@
         val subList = hitTestResult.subList(2, 4)
         assertThat(subList).hasSize(2)
 
-        assertThat(subList.toList()).isEqualTo(listOf("this", "is"))
-        assertThat(subList.contains("this")).isTrue()
-        assertThat(subList.contains("foo")).isFalse()
-        assertThat(subList.containsAll(listOf("this", "is"))).isTrue()
-        assertThat(subList.containsAll(listOf("is", "this"))).isTrue()
-        assertThat(subList.containsAll(listOf("foo", "this"))).isFalse()
-        assertThat(subList[0]).isEqualTo("this")
-        assertThat(subList[1]).isEqualTo("is")
-        assertThat(subList.indexOf("is")).isEqualTo(1)
+        assertThat(subList.toList()).isEqualTo(nodeListOf("this", "is"))
+        assertThat(subList.contains(SNode("this"))).isTrue()
+        assertThat(subList.contains(SNode("foo"))).isFalse()
+        assertThat(subList.containsAll(nodeListOf("this", "is"))).isTrue()
+        assertThat(subList.containsAll(nodeListOf("is", "this"))).isTrue()
+        assertThat(subList.containsAll(nodeListOf("foo", "this"))).isFalse()
+        assertThat(subList[0]).isEqualTo(SNode("this"))
+        assertThat(subList[1]).isEqualTo(SNode("is"))
+        assertThat(subList.indexOf(SNode("is"))).isEqualTo(1)
         assertThat(subList.isEmpty()).isFalse()
         assertThat(hitTestResult.subList(4, 4).isEmpty()).isTrue()
         assertThat(subList.subList(0, 2).toList()).isEqualTo(subList.toList())
-        assertThat(subList.subList(0, 1)[0]).isEqualTo("this")
+        assertThat(subList.subList(0, 1)[0]).isEqualTo(SNode("this"))
 
         val listIterator1 = subList.listIterator()
         assertThat(listIterator1.hasNext()).isTrue()
         assertThat(listIterator1.hasPrevious()).isFalse()
         assertThat(listIterator1.nextIndex()).isEqualTo(0)
-        assertThat(listIterator1.next()).isEqualTo("this")
+        assertThat(listIterator1.next()).isEqualTo(SNode("this"))
         assertThat(listIterator1.hasNext()).isTrue()
         assertThat(listIterator1.hasPrevious()).isTrue()
         assertThat(listIterator1.nextIndex()).isEqualTo(1)
-        assertThat(listIterator1.next()).isEqualTo("is")
+        assertThat(listIterator1.next()).isEqualTo(SNode("is"))
         assertThat(listIterator1.hasNext()).isFalse()
         assertThat(listIterator1.hasPrevious()).isTrue()
         assertThat(listIterator1.previousIndex()).isEqualTo(1)
-        assertThat(listIterator1.previous()).isEqualTo("is")
+        assertThat(listIterator1.previous()).isEqualTo(SNode("is"))
 
         val listIterator2 = subList.listIterator(1)
         assertThat(listIterator2.hasPrevious()).isTrue()
         assertThat(listIterator2.hasNext()).isTrue()
         assertThat(listIterator2.previousIndex()).isEqualTo(0)
         assertThat(listIterator2.nextIndex()).isEqualTo(1)
-        assertThat(listIterator2.previous()).isEqualTo("this")
+        assertThat(listIterator2.previous()).isEqualTo(SNode("this"))
     }
 
     @Test
     fun siblingHits() {
-        val hitTestResult = HitTestResult<String>()
+        val hitTestResult = HitTestResult()
 
         hitTestResult.siblingHits {
             hitTestResult.hit("Hello", true) {
@@ -361,7 +362,7 @@
             hitTestResult.hit("great", true) {}
         }
         assertThat(hitTestResult.toList()).isEqualTo(
-            listOf(
+            nodeListOf(
                 "Hello",
                 "World",
                 "this",
@@ -371,8 +372,8 @@
         )
     }
 
-    private fun fillHitTestResult(last: String? = null): HitTestResult<String> {
-        val hitTestResult = HitTestResult<String>()
+    private fun fillHitTestResult(last: String? = null): HitTestResult {
+        val hitTestResult = HitTestResult()
         hitTestResult.hit("Hello", true) {
             hitTestResult.hit("World", true) {
                 hitTestResult.hit("this", true) {
@@ -389,3 +390,27 @@
         return hitTestResult
     }
 }
+
+internal fun nodeListOf(vararg strings: String) = strings.map { SNode(it) }
+internal fun HitTestResult.hit(string: String, isInLayer: Boolean, childHitTest: () -> Unit) {
+    hit(SNode(string), isInLayer, childHitTest)
+}
+
+internal fun HitTestResult.hitInMinimumTouchTarget(
+    string: String,
+    distanceFromEdge: Float,
+    isInLayer: Boolean,
+    childHitTest: () -> Unit
+) = hitInMinimumTouchTarget(SNode(string), distanceFromEdge, isInLayer, childHitTest)
+
+internal fun HitTestResult.speculativeHit(
+    string: String,
+    distanceFromEdge: Float,
+    isInLayer: Boolean,
+    childHitTest: () -> Unit
+) = speculativeHit(SNode(string), distanceFromEdge, isInLayer, childHitTest)
+
+internal fun HitTestResult.contains(string: String) = contains(SNode(string))
+internal fun HitTestResult.indexOf(string: String) = indexOf(SNode(string))
+internal fun HitTestResult.lastIndexOf(string: String) = lastIndexOf(SNode(string))
+internal data class SNode(val string: String) : Modifier.Node()
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 5c31d51..3a7a1c6 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -1043,7 +1043,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(0f, 0f), hit)
 
@@ -1061,7 +1061,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(-3f, 3f), hit, true)
 
@@ -1079,7 +1079,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(0f, 3f), hit, true)
 
@@ -1097,7 +1097,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(3f, 0f), hit, true)
 
@@ -1115,7 +1115,7 @@
         )
         outerNode.add(layoutNode)
         layoutNode.onNodePlaced()
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         outerNode.hitTest(Offset(-3f, 3f), hit, true)
 
@@ -1136,7 +1136,7 @@
         )
         outerNode.add(layoutNode)
         layoutNode.onNodePlaced()
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         outerNode.hitTest(Offset(25f, 25f), hit)
 
@@ -1159,7 +1159,7 @@
         )
         outerNode.add(layoutNode)
         layoutNode.onNodePlaced()
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         outerNode.hitTest(Offset(25f, 25f), hit)
 
@@ -1187,7 +1187,7 @@
         layoutNode1.onNodePlaced()
         layoutNode2.onNodePlaced()
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Hit closer to layoutNode1
         outerNode.hitTest(Offset(5.1f, 5.5f), hit, true)
@@ -1290,7 +1290,7 @@
         layoutNode2.onNodePlaced()
         layoutNode3.onNodePlaced()
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Hit outside of layoutNode2, but near layoutNode1
         outerNode.hitTest(Offset(10.1f, 10.1f), hit, true)
@@ -1325,7 +1325,7 @@
         layoutNode1.onNodePlaced()
         layoutNode2.onNodePlaced()
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Hit layoutNode1
         outerNode.hitTest(Offset(3.95f, 3.95f), hit, true)
@@ -1354,7 +1354,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = HitTestResult<SemanticsModifierNode>()
+        val hit = HitTestResult()
 
         layoutNode.hitTestSemantics(Offset(-3f, 3f), hit)
 
@@ -1372,7 +1372,7 @@
         val layoutNode = LayoutNode(0, 0, 1, 1, semanticsModifier, DpSize(48.dp, 48.dp))
         outerNode.add(layoutNode)
         layoutNode.onNodePlaced()
-        val hit = HitTestResult<SemanticsModifierNode>()
+        val hit = HitTestResult()
 
         layoutNode.hitTestSemantics(Offset(-3f, 3f), hit)
 
@@ -1393,7 +1393,7 @@
             private val node: Modifier.Node
         ) : ModifierNodeElement<Modifier.Node>() {
             override fun create() = node
-            override fun update(node: Modifier.Node) = node
+            override fun update(node: Modifier.Node) {}
         }
         val semanticsModifierElement1 = TestSemanticsModifierElement(semanticsModifier1)
         val semanticsModifierElement2 = TestSemanticsModifierElement(semanticsModifier2)
@@ -1406,42 +1406,42 @@
         layoutNode2.onNodePlaced()
 
         // Hit closer to layoutNode1
-        val hit1 = HitTestResult<SemanticsModifierNode>()
+        val hit1 = HitTestResult()
         outerNode.hitTestSemantics(Offset(5.1f, 5.5f), hit1, true)
 
         assertThat(hit1).hasSize(1)
         assertThat(hit1[0]).isEqualTo(semanticsModifier1)
 
         // Hit closer to layoutNode2
-        val hit2 = HitTestResult<SemanticsModifierNode>()
+        val hit2 = HitTestResult()
         outerNode.hitTestSemantics(Offset(5.9f, 5.5f), hit2, true)
 
         assertThat(hit2).hasSize(1)
         assertThat(hit2[0]).isEqualTo(semanticsModifier2)
 
         // Hit closer to layoutNode1
-        val hit3 = HitTestResult<SemanticsModifierNode>()
+        val hit3 = HitTestResult()
         outerNode.hitTestSemantics(Offset(5.5f, 5.1f), hit3, true)
 
         assertThat(hit3).hasSize(1)
         assertThat(hit3[0]).isEqualTo(semanticsModifier1)
 
         // Hit closer to layoutNode2
-        val hit4 = HitTestResult<SemanticsModifierNode>()
+        val hit4 = HitTestResult()
         outerNode.hitTestSemantics(Offset(5.5f, 5.9f), hit4, true)
 
         assertThat(hit4).hasSize(1)
         assertThat(hit4[0]).isEqualTo(semanticsModifier2)
 
         // Hit inside layoutNode1
-        val hit5 = HitTestResult<SemanticsModifierNode>()
+        val hit5 = HitTestResult()
         outerNode.hitTestSemantics(Offset(4.9f, 4.9f), hit5, true)
 
         assertThat(hit5).hasSize(1)
         assertThat(hit5[0]).isEqualTo(semanticsModifier1)
 
         // Hit inside layoutNode2
-        val hit6 = HitTestResult<SemanticsModifierNode>()
+        val hit6 = HitTestResult()
         outerNode.hitTestSemantics(Offset(6.1f, 6.1f), hit6, true)
 
         assertThat(hit6).hasSize(1)
@@ -1466,14 +1466,14 @@
         layoutNode2.onNodePlaced()
 
         // Hit layoutNode1
-        val hit1 = HitTestResult<SemanticsModifierNode>()
+        val hit1 = HitTestResult()
         outerNode.hitTestSemantics(Offset(3.95f, 3.95f), hit1, true)
 
         assertThat(hit1).hasSize(1)
         assertThat(hit1[0].toModifier()).isEqualTo(semanticsModifier1)
 
         // Hit layoutNode2
-        val hit2 = HitTestResult<SemanticsModifierNode>()
+        val hit2 = HitTestResult()
         outerNode.hitTestSemantics(Offset(4.05f, 4.05f), hit2, true)
 
         assertThat(hit2).hasSize(1)
@@ -1490,7 +1490,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(-1f, -1f), hit)
         layoutNode.hitTest(Offset(0f, -1f), hit)
@@ -1518,7 +1518,7 @@
             ).apply {
                 attach(MockOwner())
             }
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         layoutNode.hitTest(Offset(-3f, -5f), hit)
         layoutNode.hitTest(Offset(0f, -5f), hit)
@@ -1593,7 +1593,7 @@
             else -> throw IllegalStateException()
         }
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -1686,8 +1686,8 @@
         val offset1 = Offset(25f, 25f)
         val offset2 = Offset(75f, 75f)
 
-        val hit1 = mutableListOf<PointerInputModifierNode>()
-        val hit2 = mutableListOf<PointerInputModifierNode>()
+        val hit1 = mutableListOf<Modifier.Node>()
+        val hit2 = mutableListOf<Modifier.Node>()
 
         // Act
 
@@ -1767,9 +1767,9 @@
         val offset2 = Offset(75f, 75f)
         val offset3 = Offset(125f, 125f)
 
-        val hit1 = mutableListOf<PointerInputModifierNode>()
-        val hit2 = mutableListOf<PointerInputModifierNode>()
-        val hit3 = mutableListOf<PointerInputModifierNode>()
+        val hit1 = mutableListOf<Modifier.Node>()
+        val hit2 = mutableListOf<Modifier.Node>()
+        val hit3 = mutableListOf<Modifier.Node>()
 
         parentLayoutNode.hitTest(offset1, hit1)
         parentLayoutNode.hitTest(offset2, hit2)
@@ -1831,9 +1831,9 @@
         val offset2 = Offset(50f, 75f)
         val offset3 = Offset(50f, 125f)
 
-        val hit1 = mutableListOf<PointerInputModifierNode>()
-        val hit2 = mutableListOf<PointerInputModifierNode>()
-        val hit3 = mutableListOf<PointerInputModifierNode>()
+        val hit1 = mutableListOf<Modifier.Node>()
+        val hit2 = mutableListOf<Modifier.Node>()
+        val hit3 = mutableListOf<Modifier.Node>()
 
         // Act
 
@@ -1895,9 +1895,9 @@
         val offset2 = Offset(75f, 50f)
         val offset3 = Offset(125f, 50f)
 
-        val hit1 = mutableListOf<PointerInputModifierNode>()
-        val hit2 = mutableListOf<PointerInputModifierNode>()
-        val hit3 = mutableListOf<PointerInputModifierNode>()
+        val hit1 = mutableListOf<Modifier.Node>()
+        val hit2 = mutableListOf<Modifier.Node>()
+        val hit3 = mutableListOf<Modifier.Node>()
 
         // Act
 
@@ -2009,7 +2009,7 @@
                 Offset(4f, 3f)
             )
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act and Assert
 
@@ -2065,7 +2065,7 @@
 
         val offset1 = Offset(50f, 75f)
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2112,7 +2112,7 @@
         layoutNode1.onNodePlaced()
         val offset1 = Offset(499f, 499f)
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2171,7 +2171,7 @@
 
         val offset1 = Offset(499f, 499f)
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2218,7 +2218,7 @@
 
         val offset = Offset(50f, 50f)
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2245,7 +2245,7 @@
 
         val offset = Offset.Zero
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2292,7 +2292,7 @@
         parent.remeasure()
         parent.replace()
 
-        val hit = mutableListOf<PointerInputModifierNode>()
+        val hit = mutableListOf<Modifier.Node>()
 
         // Act.
 
@@ -2618,6 +2618,8 @@
         TODO("Not yet implemented")
     }
 
+    val invalidatedLayers = mutableListOf<OwnedLayer>()
+
     override fun createLayer(
         drawBlock: (Canvas) -> Unit,
         invalidateParentLayer: () -> Unit
@@ -2670,6 +2672,7 @@
             }
 
             override fun invalidate() {
+                invalidatedLayers.add(this)
             }
 
             override fun destroy() {
@@ -2696,7 +2699,9 @@
         }
     }
 
+    var semanticsChanged: Boolean = false
     override fun onSemanticsChange() {
+        semanticsChanged = true
     }
 
     override fun onLayoutChange(layoutNode: LayoutNode) {
@@ -2717,10 +2722,10 @@
 @OptIn(ExperimentalComposeUiApi::class)
 private fun LayoutNode.hitTest(
     pointerPosition: Offset,
-    hitPointerInputFilters: MutableList<PointerInputModifierNode>,
+    hitPointerInputFilters: MutableList<Modifier.Node>,
     isTouchEvent: Boolean = false
 ) {
-    val hitTestResult = HitTestResult<PointerInputModifierNode>()
+    val hitTestResult = HitTestResult()
     hitTest(pointerPosition, hitTestResult, isTouchEvent)
     hitPointerInputFilters.addAll(hitTestResult)
 }
@@ -2786,7 +2791,9 @@
 }
 
 @OptIn(ExperimentalComposeUiApi::class)
-fun List<PointerInputModifierNode>.toFilters(): List<PointerInputFilter> = map { it.toFilter() }
+fun List<Modifier.Node>.toFilters(): List<PointerInputFilter> = map {
+    (it as PointerInputModifierNode).toFilter()
+}
 
 // This returns the corresponding modifier that produced the Node. This is only possible for
 // Nodes that are BackwardsCompatNodes and once we refactor semantics / pointer input to use
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierNodeElementTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierNodeElementTest.kt
index eeffd8a..63ecefa 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierNodeElementTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierNodeElementTest.kt
@@ -40,7 +40,7 @@
         ) : ModifierNodeElement<Modifier.Node>() {
             var classProperty = 0
             override fun create() = object : Modifier.Node() {}
-            override fun update(node: Modifier.Node) = node
+            override fun update(node: Modifier.Node) {}
             // We don't use equals or hashCode in this test, so bad implementations are okay.
             override fun hashCode() = 0
             override fun equals(other: Any?) = (this === other)
diff --git a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
index 27e2516..c5558b9 100644
--- a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
@@ -642,11 +642,9 @@
   }
 
   public final class MotionLayoutKt {
-    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, float progress, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, optional androidx.compose.ui.Modifier modifier, optional String? constraintSetName, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional int debugFlags, optional int optimizationLevel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, float progress, optional int debugFlags, optional androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, androidx.constraintlayout.compose.MotionLayoutState motionLayoutState, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
   }
 
   @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionLayoutScope {
@@ -684,20 +682,6 @@
     method public String? tag();
   }
 
-  @androidx.compose.runtime.Immutable @androidx.constraintlayout.compose.ExperimentalMotionApi public interface MotionLayoutState {
-    method public void animateTo(@FloatRange(from=0.0, to=1.0) float newProgress, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec);
-    method public float getCurrentProgress();
-    method public boolean isInDebugMode();
-    method public void setDebugMode(androidx.constraintlayout.compose.MotionLayoutDebugFlags motionDebugFlag);
-    method public void snapTo(@FloatRange(from=0.0, to=1.0) float newProgress);
-    property public abstract float currentProgress;
-    property public abstract boolean isInDebugMode;
-  }
-
-  public final class MotionLayoutStateKt {
-    method @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.MotionLayoutState rememberMotionLayoutState(optional Object key, optional float initialProgress, optional androidx.constraintlayout.compose.MotionLayoutDebugFlags initialDebugMode);
-  }
-
   @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionModifierScope {
     method public androidx.constraintlayout.compose.Arc getMotionArc();
     method public void keyAttributes(kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyAttributesScope,kotlin.Unit> keyAttributesContent);
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index 8242a3a..30c309a 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -491,23 +491,6 @@
     property protected final androidx.constraintlayout.compose.State state;
   }
 
-  @kotlin.PublishedApi internal interface MotionAnimationCommand {
-  }
-
-  public static final class MotionAnimationCommand.Animate implements androidx.constraintlayout.compose.MotionAnimationCommand {
-    ctor public MotionAnimationCommand.Animate(float newProgress, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec);
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getAnimationSpec();
-    method public float getNewProgress();
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec;
-    property public final float newProgress;
-  }
-
-  public static final class MotionAnimationCommand.Snap implements androidx.constraintlayout.compose.MotionAnimationCommand {
-    ctor public MotionAnimationCommand.Snap(float newProgress);
-    method public float getNewProgress();
-    property public final float newProgress;
-  }
-
   public final class MotionCarouselKt {
     method @androidx.compose.runtime.Composable public static void ItemHolder(int i, String slotPrefix, boolean showSlot, kotlin.jvm.functions.Function0<kotlin.Unit> function);
     method @androidx.compose.runtime.Composable public static void MotionCarousel(androidx.constraintlayout.compose.MotionScene motionScene, int initialSlotIndex, int numSlots, optional String backwardTransition, optional String forwardTransition, optional String slotPrefix, optional boolean showSlots, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionCarouselScope,kotlin.Unit> content);
@@ -565,9 +548,8 @@
   }
 
   public final class MotionLayoutKt {
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static inline void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, optional androidx.compose.ui.Modifier modifier, optional String? constraintSetName, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional int debugFlags, optional int optimizationLevel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static inline void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, float progress, String transitionName, int optimizationLevel, int debugFlags, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static inline void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, String transitionName, androidx.constraintlayout.compose.MotionLayoutStateImpl motionLayoutState, int optimizationLevel, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static inline void MotionLayoutCore(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, androidx.constraintlayout.compose.TransitionImpl? transition, androidx.constraintlayout.compose.MotionProgress motionProgress, androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, int optimizationLevel, boolean showBounds, boolean showPaths, boolean showKeyPositions, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void UpdateWithForcedIfNoUserChange(androidx.constraintlayout.compose.MotionProgress motionProgress, androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.constraintlayout.compose.MotionProgress createAndUpdateMotionProgress(float progress);
@@ -575,19 +557,6 @@
     method @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy motionLayoutMeasurePolicy(androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.ConstraintSet constraintSetStart, androidx.constraintlayout.compose.ConstraintSet constraintSetEnd, androidx.constraintlayout.compose.TransitionImpl transition, androidx.constraintlayout.compose.MotionProgress motionProgress, androidx.constraintlayout.compose.MotionMeasurer measurer, int optimizationLevel);
   }
 
-  @androidx.compose.runtime.Immutable @kotlin.PublishedApi internal final class MotionLayoutStateImpl {
-    ctor public MotionLayoutStateImpl(float initialProgress, androidx.constraintlayout.compose.MotionLayoutDebugFlags initialDebugMode, kotlinx.coroutines.CoroutineScope motionCoroutineScope);
-    method public void animateTo(float newProgress, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec);
-    method public float getCurrentProgress();
-    method public boolean isInDebugMode();
-    method public void setDebugMode(androidx.constraintlayout.compose.MotionLayoutDebugFlags motionDebugFlag);
-    method public void snapTo(float newProgress);
-    property public float currentProgress;
-    property @kotlin.PublishedApi internal final androidx.constraintlayout.compose.MotionLayoutDebugFlags debugMode;
-    property public boolean isInDebugMode;
-    field @kotlin.PublishedApi internal final androidx.constraintlayout.compose.MotionProgress motionProgress;
-  }
-
   @kotlin.PublishedApi internal final class MotionMeasurer extends androidx.constraintlayout.compose.Measurer {
     ctor public MotionMeasurer(androidx.compose.ui.unit.Density density);
     method public void clearConstraintSets();
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
index d3f65d8..586a4b5 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
@@ -18,6 +18,7 @@
 
 package androidx.constraintlayout.compose.demos
 
+import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
@@ -42,10 +43,10 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.DebugFlags
 import androidx.constraintlayout.compose.Dimension
 import androidx.constraintlayout.compose.ExperimentalMotionApi
 import androidx.constraintlayout.compose.MotionLayout
-import androidx.constraintlayout.compose.MotionLayoutDebugFlags
 import androidx.constraintlayout.compose.MotionScene
 import androidx.constraintlayout.compose.OnSwipe
 import androidx.constraintlayout.compose.SwipeDirection
@@ -53,7 +54,6 @@
 import androidx.constraintlayout.compose.SwipeSide
 import androidx.constraintlayout.compose.SwipeTouchUp
 import androidx.constraintlayout.compose.layoutId
-import androidx.constraintlayout.compose.rememberMotionLayoutState
 
 /**
  * Shows how to define swipe-driven transitions with `KeyPositions` and custom colors using the
@@ -65,8 +65,9 @@
     var mode by remember {
         mutableStateOf("spring")
     }
-    var toEnd by remember { mutableStateOf(true) }
-    val motionLayoutState = rememberMotionLayoutState(key = mode)
+    var animateToEnd by remember { mutableStateOf(false) }
+
+    val debugFlags = remember { mutableStateOf(DebugFlags.None) }
 
     val motionSceneContent = remember(mode) {
         // language=json5
@@ -125,21 +126,16 @@
     }
     Column {
         Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
-            Button(onClick = { motionLayoutState.snapTo(0f) }) {
-                Text(text = "Reset")
-            }
             Button(onClick = {
-                val target = if (toEnd) 1f else 0f
-                motionLayoutState.animateTo(target, tween(2000))
-                toEnd = !toEnd
+                animateToEnd = !animateToEnd
             }) {
-                Text(text = if (toEnd) "End" else "Start")
+                Text(text = if (animateToEnd) "Start" else "End")
             }
             Button(onClick = {
-                if (motionLayoutState.isInDebugMode) {
-                    motionLayoutState.setDebugMode(MotionLayoutDebugFlags.NONE)
+                if (debugFlags.value == DebugFlags.All) {
+                    debugFlags.value = DebugFlags.None
                 } else {
-                    motionLayoutState.setDebugMode(MotionLayoutDebugFlags.SHOW_ALL)
+                    debugFlags.value = DebugFlags.All
                 }
             }) {
                 Text("Debug")
@@ -157,8 +153,12 @@
             modifier = Modifier
                 .fillMaxWidth()
                 .weight(1.0f, fill = true),
-            motionLayoutState = motionLayoutState,
-            motionScene = MotionScene(content = motionSceneContent)
+            progress = animateFloatAsState(
+                targetValue = if (animateToEnd) 1f else 0f,
+                tween(1000)
+            ).value,
+            motionScene = MotionScene(content = motionSceneContent),
+            debugFlags = debugFlags.value
         ) {
             Box(
                 modifier = Modifier
@@ -166,7 +166,6 @@
                     .layoutId("box")
             )
         }
-        Text(text = "Current progress: ${motionLayoutState.currentProgress}")
     }
 }
 
@@ -283,7 +282,8 @@
                     onTouchUp = touchUp
                 )
             }
-        }
+        },
+        progress = 0f
     ) {
         val progress = customFloat("title", "mValue")
         val textBackColor = customColor("title", "back")
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutStateTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutStateTest.kt
deleted file mode 100644
index 46ccfe2..0000000
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutStateTest.kt
+++ /dev/null
@@ -1,125 +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.
- */
-
-package androidx.constraintlayout.compose
-
-import androidx.compose.animation.core.tween
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material.Text
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import org.intellij.lang.annotations.Language
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@Language("json5")
-private const val SCENE_WITH_BOX =
-    """
-{
-  ConstraintSets: {
-    start: {
-      box: {
-        width: 50, height: 50,
-        start: ['parent', 'start', 10],
-        top: ['parent', 'top', 10]
-      }
-    },
-    end: {
-      box: {
-        width: 50, height: 50,
-        end: ['parent', 'end', 10],
-        top: ['parent', 'top', 10]
-      }
-    }
-  },
-  Transitions: {
-    default: {
-      from: 'start',
-      to: 'end'
-    }
-  }
-}
-"""
-
-/**
- * Run with Pixel 3 API 30.
- */
-@MediumTest
-@OptIn(ExperimentalMotionApi::class)
-@RunWith(AndroidJUnit4::class)
-class MotionLayoutStateTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Before
-    fun setup() {
-        isDebugInspectorInfoEnabled = true
-    }
-
-    @After
-    fun teardown() {
-        isDebugInspectorInfoEnabled = false
-    }
-
-    @Test
-    fun stateProgressTestAndInvalidateWithKey() {
-        var statePointer: MotionLayoutState? = null
-        val myKey = mutableStateOf(0)
-        rule.setContent {
-            // Instantiate the MotionLayoutState with an initial progress
-            val motionState = rememberMotionLayoutState(initialProgress = 0.5f, key = myKey.value)
-            statePointer = motionState
-            MotionLayout(
-                modifier = Modifier.fillMaxSize(),
-                motionLayoutState = motionState,
-                motionScene = MotionScene(SCENE_WITH_BOX)
-            ) {
-                Box(modifier = Modifier.layoutId("box"))
-            }
-            // Text composable to track the progress
-            Text(text = "Progress: ${(motionState.currentProgress * 100).toInt()}")
-        }
-        rule.waitForIdle()
-        // The Text Composable with the initial progress
-        rule.onNodeWithText("Progress: 50").assertExists()
-
-        rule.runOnUiThread {
-            // Send a command, outside the original Compose context to animate to 100%
-            statePointer?.animateTo(1f, tween(100))
-        }
-
-        rule.waitForIdle()
-        // The Text Composable with the last observed progress
-        rule.onNodeWithText("Progress: 100").assertExists()
-
-        rule.runOnUiThread {
-            // Invalidate the state by changing the key
-            myKey.value = 1
-        }
-        rule.waitForIdle()
-        // The Text Composable with the progress value reset to initial (50%)
-        rule.onNodeWithText("Progress: 50").assertExists()
-    }
-}
\ No newline at end of file
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 61b5b95..051dce7 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
@@ -18,10 +18,10 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.LayoutScopeMarker
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
@@ -71,17 +71,59 @@
 }
 
 /**
- * Layout that interpolate its children layout given two sets of constraint and
- * a progress (from 0 to 1)
+ * Layout that can animate between two different layout states described in [ConstraintSet]s.
+ *
+ * &nbsp;
+ *
+ * The animation is driven by the [progress] value, so it will typically be a result of
+ * using an [Animatable][androidx.compose.animation.core.Animatable] or
+ * [animateFloatAsState][androidx.compose.animation.core.animateFloatAsState]:
+ * ```
+ *  var animateToEnd by remember { mutableStateOf(false) }
+ *  MotionLayout(
+ *      start = ConstraintSet {
+ *          constrain(createRefFor("button")) {
+ *              top.linkTo(parent.top)
+ *          }
+ *      },
+ *      end = ConstraintSet {
+ *          constrain(createRefFor("button")) {
+ *              bottom.linkTo(parent.bottom)
+ *          }
+ *      },
+ *      progress = animateFloatAsState(if (animateToEnd) 1f else 0f).value,
+ *      modifier = Modifier.fillMaxSize()
+ *  ) {
+ *      Button(onClick = { animateToEnd = !animateToEnd }, Modifier.layoutId("button")) {
+ *          Text("Hello, World!")
+ *      }
+ *  }
+ * ```
+ *
+ * Note that you must use [Modifier.layoutId][androidx.compose.ui.layout.layoutId] to bind the
+ * the references used in the [ConstraintSet]s to the Composable.
+ *
+ * @param start ConstraintSet that defines the layout at 0f progress.
+ * @param end ConstraintSet that defines the layout at 1f progress.
+ * @param progress Sets the interpolated position of the layout between the ConstraintSets.
+ * @param modifier Modifier to apply to this layout node.
+ * @param transition Defines the interpolation parameters between the [ConstraintSet]s to achieve
+ * fine-tuned animations.
+ * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
+ * [Optimizer.OPTIMIZATION_STANDARD] by default.
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param content The content to be laid out by MotionLayout, note that each layout Composable
+ * should be bound to an ID defined in the [ConstraintSet]s using
+ * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
  */
 @ExperimentalMotionApi
 @Composable
 inline fun MotionLayout(
     start: ConstraintSet,
     end: ConstraintSet,
+    progress: Float,
     modifier: Modifier = Modifier,
     transition: Transition? = null,
-    progress: Float,
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
     crossinline content: @Composable MotionLayoutScope.() -> Unit
@@ -103,8 +145,56 @@
 }
 
 /**
- * Layout that animates the default transition of a [MotionScene] with a progress value (from 0 to
- * 1).
+ * Layout that can animate between multiple [ConstraintSet]s as defined by [Transition]s in the
+ * given [MotionScene].
+ *
+ * &nbsp;
+ *
+ * The animation is driven by the [progress] value, so it will typically be a result of
+ * using an [Animatable][androidx.compose.animation.core.Animatable] or
+ * [animateFloatAsState][androidx.compose.animation.core.animateFloatAsState]:
+ * ```
+ *  var animateToEnd by remember { mutableStateOf(false) }
+ *  MotionLayout(
+ *      motionScene = MotionScene {
+ *          val buttonRef = createRefFor("button")
+ *          defaultTransition(
+ *              from = constraintSet {
+ *                  constrain(buttonRef) {
+ *                      top.linkTo(parent.top)
+ *                  }
+ *              },
+ *              to = constraintSet {
+ *                  constrain(buttonRef) {
+ *                      bottom.linkTo(parent.bottom)
+ *                  }
+ *              }
+ *          )
+ *      },
+ *      progress = animateFloatAsState(if (animateToEnd) 1f else 0f).value,
+ *      modifier = Modifier.fillMaxSize()
+ *  ) {
+ *      Button(onClick = { animateToEnd = !animateToEnd }, Modifier.layoutId("button")) {
+ *          Text("Hello, World!")
+ *      }
+ *  }
+ * ```
+ *
+ * Note that you must use [Modifier.layoutId][androidx.compose.ui.layout.layoutId] to bind the
+ * the references used in the [ConstraintSet]s to the Composable.
+ *
+ * @param motionScene Holds all the layout states defined in [ConstraintSet]s and the
+ * interpolation associated between them (known as [Transition]s).
+ * @param progress Sets the interpolated position of the layout between the ConstraintSets.
+ * @param modifier Modifier to apply to this layout node.
+ * @param transitionName The name of the transition to apply on the layout. By default, it will
+ * target the transition defined with [MotionSceneScope.defaultTransition].
+ * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
+ * [Optimizer.OPTIMIZATION_STANDARD] by default.
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param content The content to be laid out by MotionLayout, note that each layout Composable
+ * should be bound to an ID defined in the [ConstraintSet]s using
+ * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
  */
 @ExperimentalMotionApi
 @Composable
@@ -129,83 +219,85 @@
 }
 
 /**
- * Layout that takes a MotionScene and animates by providing a [constraintSetName] to animate to.
+ * Layout that can animate between multiple [ConstraintSet]s as defined by [Transition]s in the
+ * given [MotionScene].
  *
- * During recomposition, MotionLayout will interpolate from whichever ConstraintSet it is currently
- * in, to [constraintSetName].
+ * &nbsp;
  *
- * Typically the first value of [constraintSetName] should match the start ConstraintSet in the
- * default transition, or be null.
+ * The animation is driven based on the given [constraintSetName]. During recomposition,
+ * MotionLayout will interpolate from whichever [ConstraintSet] it currently is, to the one
+ * corresponding to [constraintSetName]. So, a null [constraintSetName] will result in no changes.
  *
- * Animation is run by [animationSpec], and will only start another animation once any other ones
- * are finished. Use [finishedAnimationListener] to know when a transition has stopped.
+ * ```
+ *  var name by remember { mutableStateOf(0) }
+ *  MotionLayout(
+ *      motionScene = MotionScene {
+ *          val buttonRef = createRefFor("button")
+ *          val initialStart = constraintSet("0") {
+ *              constrain(buttonRef) {
+ *                  centerHorizontallyTo(parent, bias = 0f)
+ *                  centerVerticallyTo(parent, bias = 0f)
+ *              }
+ *          }
+ *          val initialEnd = constraintSet("1") {
+ *              constrain(buttonRef) {
+ *                  centerHorizontallyTo(parent, bias = 0f)
+ *                  centerVerticallyTo(parent, bias = 1f)
+ *              }
+ *          }
+ *          constraintSet("2") {
+ *              constrain(buttonRef) {
+ *                  centerHorizontallyTo(parent, bias = 1f)
+ *                  centerVerticallyTo(parent, bias = 0f)
+ *              }
+ *          }
+ *          constraintSet("3") {
+ *              constrain(buttonRef) {
+ *                  centerHorizontallyTo(parent, bias = 1f)
+ *                  centerVerticallyTo(parent, bias = 1f)
+ *              }
+ *          }
+ *          // We need at least the default transition to define the initial state
+ *          defaultTransition(initialStart, initialEnd)
+ *      },
+ *      constraintSetName = name.toString(),
+ *      animationSpec = tween(1200),
+ *      modifier = Modifier.fillMaxSize()
+ *  ) {
+ *      // Switch to a random ConstraintSet on click
+ *      Button(onClick = { name = IntRange(0, 3).random() }, Modifier.layoutId("button")) {
+ *          Text("Hello, World!")
+ *      }
+ *  }
+ * ```
+ *
+ * Animations are run one after the other, if multiple are queued, only the last one will be
+ * executed. You may use [finishedAnimationListener] to know whenever an animation is finished.
+ *
+ * @param motionScene Holds all the layout states defined in [ConstraintSet]s and the
+ * interpolation associated between them (known as [Transition]s).
+ * @param constraintSetName The name of the [ConstraintSet] to animate to. Null for no animation.
+ * @param animationSpec Specifies how the internal progress value is animated.
+ * @param modifier Modifier to apply to this layout node.
+ * @param finishedAnimationListener Called when an animation triggered by a change in
+ * [constraintSetName] has ended.
+ * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
+ * [Optimizer.OPTIMIZATION_STANDARD] by default.
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param content The content to be laid out by MotionLayout, note that each layout Composable
+ * should be bound to an ID defined in the [ConstraintSet]s using
+ * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
  */
 @ExperimentalMotionApi
 @Composable
 inline fun MotionLayout(
     motionScene: MotionScene,
+    constraintSetName: String?,
+    animationSpec: AnimationSpec<Float>,
     modifier: Modifier = Modifier,
-    constraintSetName: String? = null,
-    animationSpec: AnimationSpec<Float> = tween(),
-    debugFlags: DebugFlags = DebugFlags.None,
-    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
     noinline finishedAnimationListener: (() -> Unit)? = null,
-    crossinline content: @Composable (MotionLayoutScope.() -> Unit)
-) {
-    MotionLayoutCore(
-        motionScene = motionScene,
-        constraintSetName = constraintSetName,
-        animationSpec = animationSpec,
-        debugFlags = debugFlags,
-        modifier = modifier,
-        optimizationLevel = optimizationLevel,
-        finishedAnimationListener = finishedAnimationListener,
-        content = content
-    )
-}
-
-@ExperimentalMotionApi
-@Composable
-inline fun MotionLayout(
-    start: ConstraintSet,
-    end: ConstraintSet,
-    modifier: Modifier = Modifier,
-    transition: Transition? = null,
-    progress: Float,
-    debugFlags: DebugFlags = DebugFlags.None,
-    informationReceiver: LayoutInformationReceiver? = null,
-    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
-    crossinline content: @Composable (MotionLayoutScope.() -> Unit)
-) {
-    val motionProgress = createAndUpdateMotionProgress(progress = progress)
-    MotionLayoutCore(
-        start = start,
-        end = end,
-        transition = transition as? TransitionImpl,
-        motionProgress = motionProgress,
-        informationReceiver = informationReceiver,
-        optimizationLevel = optimizationLevel,
-        showBounds = debugFlags.showBounds,
-        showPaths = debugFlags.showPaths,
-        showKeyPositions = debugFlags.showKeyPositions,
-        modifier = modifier,
-        content = content
-    )
-}
-
-@ExperimentalMotionApi
-@PublishedApi
-@Composable
-@Suppress("UnavailableSymbol")
-internal inline fun MotionLayoutCore(
-    @Suppress("HiddenTypeParameter")
-    motionScene: MotionScene,
-    modifier: Modifier = Modifier,
-    constraintSetName: String? = null,
-    animationSpec: AnimationSpec<Float> = tween(),
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
-    noinline finishedAnimationListener: (() -> Unit)? = null,
     @Suppress("HiddenTypeParameter")
     crossinline content: @Composable (MotionLayoutScope.() -> Unit)
 ) {
@@ -292,6 +384,38 @@
     )
 }
 
+@PublishedApi
+@ExperimentalMotionApi
+@Composable
+@Suppress("UnavailableSymbol")
+internal inline fun MotionLayout(
+    start: ConstraintSet,
+    end: ConstraintSet,
+    progress: Float,
+    modifier: Modifier = Modifier,
+    @Suppress("HiddenTypeParameter")
+    transition: Transition? = null,
+    debugFlags: DebugFlags = DebugFlags.None,
+    informationReceiver: LayoutInformationReceiver? = null,
+    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
+    @Suppress("HiddenTypeParameter")
+    crossinline content: @Composable MotionLayoutScope.() -> Unit
+) {
+    MotionLayoutCore(
+        start = start,
+        end = end,
+        transition = transition as? TransitionImpl,
+        motionProgress = createAndUpdateMotionProgress(progress = progress),
+        informationReceiver = informationReceiver,
+        optimizationLevel = optimizationLevel,
+        showBounds = debugFlags.showBounds,
+        showPaths = debugFlags.showPaths,
+        showKeyPositions = debugFlags.showKeyPositions,
+        modifier = modifier,
+        content = content
+    )
+}
+
 @ExperimentalMotionApi
 @PublishedApi
 @Composable
@@ -337,71 +461,6 @@
 }
 
 @ExperimentalMotionApi
-@Composable
-inline fun MotionLayout(
-    motionScene: MotionScene,
-    motionLayoutState: MotionLayoutState,
-    modifier: Modifier = Modifier,
-    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
-    crossinline content: @Composable MotionLayoutScope.() -> Unit
-) {
-    MotionLayoutCore(
-        modifier = modifier,
-        optimizationLevel = optimizationLevel,
-        motionLayoutState = motionLayoutState as MotionLayoutStateImpl,
-        motionScene = motionScene,
-        transitionName = "default",
-        content = content
-    )
-}
-
-@PublishedApi
-@ExperimentalMotionApi
-@Composable
-@Suppress("UnavailableSymbol")
-internal inline fun MotionLayoutCore(
-    @Suppress("HiddenTypeParameter")
-    motionScene: MotionScene,
-    transitionName: String,
-    motionLayoutState: MotionLayoutStateImpl,
-    optimizationLevel: Int,
-    modifier: Modifier,
-    @Suppress("HiddenTypeParameter")
-    crossinline content: @Composable MotionLayoutScope.() -> Unit
-) {
-    val transition = remember(motionScene, transitionName) {
-        motionScene.getTransitionInstance(transitionName)
-    }
-
-    val start = remember(motionScene, transition) {
-        val startId = transition?.getStartConstraintSetId() ?: "start"
-        motionScene.getConstraintSetInstance(startId)
-    }
-    val end = remember(motionScene, transition) {
-        val endId = transition?.getEndConstraintSetId() ?: "end"
-        motionScene.getConstraintSetInstance(endId)
-    }
-
-    if (start == null || end == null) {
-        return
-    }
-    val showDebug = motionLayoutState.debugMode == MotionLayoutDebugFlags.SHOW_ALL
-    MotionLayoutCore(
-        start = start,
-        end = end,
-        transition = transition as? TransitionImpl,
-        motionProgress = motionLayoutState.motionProgress,
-        informationReceiver = motionScene as? JSONMotionScene,
-        optimizationLevel = optimizationLevel,
-        showBounds = showDebug,
-        showPaths = showDebug,
-        showKeyPositions = showDebug,
-        modifier = modifier,
-        content = content
-    )
-}
-
-@ExperimentalMotionApi
 @PublishedApi
 @Composable
 @Suppress("UnavailableSymbol")
@@ -474,7 +533,7 @@
     var doShowPaths = showPaths
     var doShowKeyPositions = showKeyPositions
 
-    if (forcedDebug != null) {
+    if (forcedDebug != null && forcedDebug != MotionLayoutDebugFlags.UNKNOWN) {
         doShowBounds = forcedDebug === MotionLayoutDebugFlags.SHOW_ALL
         doShowPaths = doShowBounds
         doShowKeyPositions = doShowBounds
@@ -950,14 +1009,45 @@
 }
 
 /**
+ * Internal representation to read and set values for the progress.
+ */
+@PublishedApi
+internal interface MotionProgress {
+    // TODO: Since this class has no other uses anymore, consider to substitute it with a simple
+    //  MutableState<Float>
+
+    val currentProgress: Float
+
+    fun updateProgress(newProgress: Float)
+
+    companion object {
+        fun fromMutableState(mutableProgress: MutableState<Float>): MotionProgress =
+            fromState(mutableProgress) { mutableProgress.value = it }
+
+        fun fromState(
+            progressState: State<Float>,
+            onUpdate: (newProgress: Float) -> Unit
+        ): MotionProgress =
+            object : MotionProgress {
+                override val currentProgress: Float
+                    get() = progressState.value
+
+                override fun updateProgress(newProgress: Float) {
+                    onUpdate(newProgress)
+                }
+            }
+    }
+}
+
+/**
  * Flags to use with MotionLayout to enable visual debugging.
  *
  * @property showBounds
  * @property showPaths
  * @property showKeyPositions
  *
- * @see None
- * @see All
+ * @see DebugFlags.None
+ * @see DebugFlags.All
  */
 @ExperimentalMotionApi
 @JvmInline
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayoutState.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayoutState.kt
deleted file mode 100644
index 8f85a69..0000000
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayoutState.kt
+++ /dev/null
@@ -1,284 +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.
- */
-
-package androidx.constraintlayout.compose
-
-import androidx.annotation.FloatRange
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
-
-/**
- * Class used to read and manipulate the state of a MotionLayout Composable.
- */
-@Immutable
-@ExperimentalMotionApi
-interface MotionLayoutState {
-    // TODO: Add API to listen to finished Transition animation
-    // TODO: Add API to know if MotionLayout is on an ongoing animation
-
-    /**
-     * Observable value for the animation progress of the current MotionLayout Transition.
-     *
-     * Where 0.0f is the start of the Transition.
-     *
-     * And 1.0f is the end of the Transition.
-     *
-     * Beware that reading a 0 or 1 does not imply that a Transition animation has ended.
-     */
-    val currentProgress: Float
-
-    /**
-     * Observable value to indicate if MotionLayout is in a debugging mode.
-     *
-     * False by default.
-     */
-    val isInDebugMode: Boolean
-
-    /**
-     * Change the debugging mode.
-     *
-     * Note that this causes an internal recomposition of the MotionLayout modifiers, cancelling
-     * events like swipe handling. Also, debugging may add overhead to measuring and/or drawing.
-     *
-     * Set [MotionLayoutDebugFlags.NONE] to deactivate any ongoing debugging.
-     *
-     * @see MotionLayoutDebugFlags
-     */
-    fun setDebugMode(motionDebugFlag: MotionLayoutDebugFlags)
-
-    /**
-     * Set the animation progress to the given [newProgress] without animating. The value change
-     * will be instant.
-     *
-     * Calls to this method will cancel any ongoing animation.
-     */
-    fun snapTo(@FloatRange(from = 0.0, to = 1.0) newProgress: Float)
-
-    /**
-     * Animate the progress to the given [newProgress] using [animationSpec].
-     *
-     * Repeated calls to this method will cancel previous ongoing animations.
-     */
-    fun animateTo(
-        @FloatRange(from = 0.0, to = 1.0) newProgress: Float,
-        animationSpec: AnimationSpec<Float>
-    )
-}
-
-/**
- * Implementation of [MotionLayoutState] with additional properties used by MotionLayout internals.
- */
-@Immutable
-@ExperimentalMotionApi
-@PublishedApi
-internal class MotionLayoutStateImpl(
-    initialProgress: Float,
-    initialDebugMode: MotionLayoutDebugFlags,
-    private val motionCoroutineScope: CoroutineScope
-) : MotionLayoutState {
-    /**
-     * The underlying object that holds the progress value for a [MotionLayout] Composable.
-     *
-     * Manipulated using the [Animatable] API, exposed internally with
-     * [motionProgress]; and externally with [currentProgress], [animateTo] and [snapTo].
-     */
-    private val animatableProgress = Animatable(initialProgress)
-
-    /**
-     * Channel to allow scheduling Animation Commands into [motionCoroutineScope].
-     */
-    private val channel = Channel<MotionAnimationCommand>(capacity = Channel.UNLIMITED).also {
-        motionCoroutineScope.launch {
-            while (coroutineContext.isActive) {
-                // Wait for the next Command
-                val stateCommand = it.receive()
-
-                // Handle the command with `launch` to avoid blocking this scope, when a new Command
-                // is received and launched, Animatable will cancel any running animations from
-                // previous Commands
-                launch {
-                    when (stateCommand) {
-                        is MotionAnimationCommand.Animate -> {
-                            animatableProgress.animateTo(
-                                targetValue = stateCommand.newProgress,
-                                animationSpec = stateCommand.animationSpec
-                            )
-                        }
-                        is MotionAnimationCommand.Snap -> {
-                            animatableProgress.snapTo(targetValue = stateCommand.newProgress)
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * [MutableState] for the debug mode.
-     */
-    private val debugModeState: MutableState<MotionLayoutDebugFlags> =
-        mutableStateOf(initialDebugMode)
-
-    /**
-     * Internal observable debug mode.
-     *
-     * @see MotionLayoutDebugFlags
-     */
-    @PublishedApi
-    internal val debugMode: MotionLayoutDebugFlags
-        get() = debugModeState.value
-
-    /**
-     * Object used by MotionLayout internals to read and update the progress.
-     */
-    @PublishedApi
-    internal val motionProgress: MotionProgress =
-        MotionProgress.fromState(animatableProgress.asState(), ::snapTo)
-
-    override val currentProgress: Float
-        get() = animatableProgress.value
-
-    override val isInDebugMode: Boolean
-        get() = debugModeState.value == MotionLayoutDebugFlags.SHOW_ALL
-
-    override fun setDebugMode(motionDebugFlag: MotionLayoutDebugFlags) {
-        debugModeState.value = motionDebugFlag
-    }
-
-    override fun snapTo(newProgress: Float) {
-        channel.trySend(MotionAnimationCommand.Snap(newProgress))
-    }
-
-    override fun animateTo(newProgress: Float, animationSpec: AnimationSpec<Float>) {
-        channel.trySend(MotionAnimationCommand.Animate(newProgress, animationSpec))
-    }
-}
-
-/**
- * Returns a [MotionLayoutState], when passed to a [MotionLayout] Composable it can be used to
- * observe and animate its internal progress value.
- *
- * - To animate on click:
- * ```
- * @Composable
- * fun MyComposable() {
- *   val motionState = rememberMotionLayoutState()
- *   Column {
- *     MotionLayout(motionLayoutState = motionState, motionScene = MotionScene(<your-json>)) {
- *       <your-composables>
- *     }
- *     Button(
- *       // Animate the associated MotionLayout to end (progress = 1f)
- *       onClick = { motionState.animateTo(1f, spring()) }
- *     ) {
- *       Text(text = "Send")
- *     }
- *   }
- * }
- * ```
- * - Use the current progress value:
- * ```
- * @Composable
- * fun MyComposable() {
- *   val motionState = rememberMotionLayoutState()
- *   Column {
- *     MotionLayout(motionLayoutState = motionState, motionScene = MotionScene(<your-json>)) {
- *       <your-composables>
- *     }
- *     // Text will recompose during MotionLayout animation with the current progress value
- *     Text(text = "Value: ${motionState.currentProgress}")
- *   }
- * }
- * ```
- *
- * Returns the same instance if [key] is equal to the previous composition, otherwise produces and
- * remembers a new instance (with the given initial values).
- */
-@ExperimentalMotionApi
-@Composable
-fun rememberMotionLayoutState(
-    key: Any = Unit,
-    initialProgress: Float = 0f,
-    initialDebugMode: MotionLayoutDebugFlags = MotionLayoutDebugFlags.NONE
-): MotionLayoutState {
-    val coroutineScope = rememberCoroutineScope()
-    return remember(key) {
-        MotionLayoutStateImpl(
-            initialProgress = initialProgress,
-            initialDebugMode = initialDebugMode,
-            motionCoroutineScope = coroutineScope
-        )
-    }
-}
-
-/**
- * Convenience interface used for [MotionLayoutStateImpl.channel], to handle calls to [Animatable].
- */
-@PublishedApi
-internal interface MotionAnimationCommand {
-
-    /**
-     * Required parameters used for [Animatable.animateTo].
-     */
-    class Animate(
-        val newProgress: Float,
-        val animationSpec: AnimationSpec<Float>
-    ) : MotionAnimationCommand
-
-    /**
-     * Required parameters used for [Animatable.snapTo].
-     */
-    class Snap(val newProgress: Float) : MotionAnimationCommand
-}
-
-/**
- * Internal representation to read and set values for the progress.
- */
-@PublishedApi
-internal interface MotionProgress {
-    val currentProgress: Float
-
-    fun updateProgress(newProgress: Float)
-
-    companion object {
-        fun fromMutableState(mutableProgress: MutableState<Float>): MotionProgress =
-            fromState(mutableProgress) { mutableProgress.value = it }
-
-        fun fromState(
-            progressState: State<Float>,
-            onUpdate: (newProgress: Float) -> Unit
-        ): MotionProgress =
-            object : MotionProgress {
-                override val currentProgress: Float
-                    get() = progressState.value
-
-                override fun updateProgress(newProgress: Float) {
-                    onUpdate(newProgress)
-                }
-            }
-    }
-}
\ No newline at end of file
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java b/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
index 23fa07b..f64ccb0 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
@@ -34,7 +34,6 @@
 import androidx.core.i18n.DateTimeFormatterSkeletonOptions.Year;
 import androidx.core.os.BuildCompat;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -86,12 +85,7 @@
     }
 
     @Test @SmallTest
-    @SdkSuppress(maxSdkVersion = 33) // b/262909049: Do not run this test on pre-release Android U.
     public void testSkeletonOptions() {
-        if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
-            return; // b/262909049: Do not run this test on pre-release Android U.
-        }
-
         final DateTimeFormatterSkeletonOptions.Builder builder =
                 new DateTimeFormatterSkeletonOptions.Builder()
                         .setYear(Year.NUMERIC)
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
index cead280..4f0a3ea 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
@@ -89,7 +89,9 @@
         val jdkFormatter = DateFormat.getTimeInstance(javaStyle, locale)
         val options = DateTimeFormatterJdkStyleOptions.createTimeInstance(javaStyle)
         val compatFormatter = DateTimeFormatter(options, locale)
-        assertEquals(jdkFormatter.format(testCalendar.time), compatFormatter.format(testCalendar))
+        assertEquals(
+            Helper.normalizeNnbsp(jdkFormatter.format(testCalendar.time)),
+            Helper.normalizeNnbsp(compatFormatter.format(testCalendar)))
     }
 
     private fun checkDateTime(
@@ -101,7 +103,9 @@
         val options =
             DateTimeFormatterJdkStyleOptions.createDateTimeInstance(javaDateStyle, javaTimeStyle)
         val compatFormatter = DateTimeFormatter(options, locale)
-        assertEquals(jdkFormatter.format(testCalendar.time), compatFormatter.format(testCalendar))
+        assertEquals(
+            Helper.normalizeNnbsp(jdkFormatter.format(testCalendar.time)),
+            Helper.normalizeNnbsp(compatFormatter.format(testCalendar)))
     }
 
     @Test @SmallTest
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
index 265f46b..6860ebe 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
@@ -40,12 +40,7 @@
     )
 
     @Test @SmallTest
-    @SdkSuppress(maxSdkVersion = 33) // b/262909049: Do not run this test on pre-release Android U.
     fun test() {
-        if (Build.VERSION.SDK_INT == 33 && Build.VERSION.CODENAME != "REL") {
-            return // b/262909049: Do not run this test on pre-release Android U.
-        }
-
         val commonFormats = mapOf(
             DateTimeFormatterCommonOptions.ABBR_MONTH_WEEKDAY_DAY to "Sun, Sep 19",
             DateTimeFormatterCommonOptions.ABBR_MONTH_DAY to "Sep 19",
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
index 3671c09..9534b9f 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
@@ -205,35 +205,29 @@
         val enUsForceH23 = Locale.forLanguageTag("en-US-u-hc-h23")
         val enUsForceH24 = Locale.forLanguageTag("en-US-u-hc-h24")
 
-        val expectedUs: String
-        val expectedUs11: String
-        val expectedUs12: String
-        val expectedUs23: String
-        val expectedUs24: String
-        // TODO: check this. Is `-u-hc-` not honored at all? File bug, maybe implement workaround.
-        if (BuildCompat.isAtLeastU()) {
-            expectedUs = "9:42:12\u202FPM"
-        } else {
-            expectedUs = "9:42:12 PM"
-        }
-        expectedUs11 = expectedUs
-        expectedUs12 = expectedUs
-        expectedUs23 = expectedUs
-        expectedUs24 = expectedUs
+        val expectedUs: String = "9:42:12 PM"
+        val expectedUs11: String = expectedUs
+        val expectedUs12: String = expectedUs
+        // TODO: check this. Is `-u-hc-` not honored at all?
+        // Official bug: https://unicode-org.atlassian.net/browse/ICU-11870
+        // It only manifests for the predefined formats (`DateFormat.MEDIUM` and so on),
+        // not for patterns generated from skeletons.
+        val expectedUs23: String = expectedUs
+        val expectedUs24: String = expectedUs
 
         var formatter: java.text.DateFormat
 
         // Formatting with style does not honor the uc overrides
         formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, Locale.US)
-        assertEquals(expectedUs, formatter.format(testMillis))
+        assertEquals(expectedUs, Helper.normalizeNnbsp(formatter.format(testMillis)))
         formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH11)
-        assertEquals(expectedUs11, formatter.format(testMillis))
+        assertEquals(expectedUs11, Helper.normalizeNnbsp(formatter.format(testMillis)))
         formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH12)
-        assertEquals(expectedUs12, formatter.format(testMillis))
+        assertEquals(expectedUs12, Helper.normalizeNnbsp(formatter.format(testMillis)))
         formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH23)
-        assertEquals(expectedUs23, formatter.format(testMillis))
+        assertEquals(expectedUs23, Helper.normalizeNnbsp(formatter.format(testMillis)))
         formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH24)
-        assertEquals(expectedUs24, formatter.format(testMillis))
+        assertEquals(expectedUs24, Helper.normalizeNnbsp(formatter.format(testMillis)))
     }
 
     @Test @SmallTest
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt
new file mode 100644
index 0000000..cea0afd5
--- /dev/null
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package androidx.core.i18n
+
+object Helper {
+    /*
+     * This method changes all instances of U+202F to U+0020.
+     *
+     * Android U takes ICU 71.1, which uses NNBSP (NARROW NO-BREAK SPACE, U+202F)
+     * betwee time and day cycle (for example 9:42\u202FPM)
+     *
+     * The Android `java.text.DateFormat` was patched to not use nnbsp (U+202F)
+     * in Android U, but ICU still returns times with U+202F.
+     * So this would give different results, but it is expected.
+     * In time this will probably go away (as the newer Android images propagate everywhere).
+     *
+     * And, since the patch happened without changing the Android version (pre-release),
+     * there are some Android U images that use space and some that use NNBSP.
+     * So testing the version is not enough to reliably tell if we will get.
+     */
+    fun normalizeNnbsp(text: String): String {
+        return text.replace("\u202F", " ")
+    }
+}
\ No newline at end of file
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 5b50848..9d411f1 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -885,7 +885,6 @@
   }
 
   public final class NotificationManagerCompat {
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS) @VisibleForTesting protected NotificationManagerCompat(android.app.NotificationManager, android.content.Context);
     method public boolean areNotificationsEnabled();
     method public void cancel(int);
     method public void cancel(String?, int);
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
index f9226fe..3628701c 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
@@ -524,4 +524,15 @@
         final ShortcutInfo shortcut = compat.toShortcutInfo();
         assertTrue(shortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER));
     }
+
+    @Test
+    public void testSetCategoriesAndAddCapabilityBinding() {
+        HashSet<String> categories = new HashSet<>();
+        categories.add("a");
+        mBuilder.setActivity(new ComponentName(mContext, TestActivity.class))
+                .setCategories(categories)
+                .addCapabilityBinding("b")
+                .build();
+        assertEquals(1, categories.size());
+    }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/hardware/display/DisplayManagerCompatTest.kt b/core/core/src/androidTest/java/androidx/core/hardware/display/DisplayManagerCompatTest.kt
new file mode 100644
index 0000000..4c92a70
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/hardware/display/DisplayManagerCompatTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.hardware.display
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertSame
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DisplayManagerCompatTest {
+
+    private lateinit var context: Context
+
+    @Before
+    fun setup() {
+        context = InstrumentationRegistry.getInstrumentation().context
+    }
+
+    @Test
+    fun testGetInstance() {
+        val displayManagerA = DisplayManagerCompat.getInstance(context)
+        assertNotNull(displayManagerA)
+
+        val displayManagerB = DisplayManagerCompat.getInstance(context)
+        assertSame(displayManagerA, displayManagerB)
+    }
+
+    @Test
+    fun testGetDisplay() {
+        val displayManager = DisplayManagerCompat.getInstance(context)
+        assertNotNull(displayManager)
+
+        val displays = displayManager.displays
+        assertNotNull(displays)
+
+        // If this device has displays, make sure we can obtain them. This is objectively an
+        // integration test, but it's the best we can do given the platform's testability.
+        displays.forEach { display ->
+            val actualDisplay = displayManager.getDisplay(display.displayId)
+            assertNotNull(actualDisplay)
+        }
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/contentcapture/ContentCaptureSessionCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/contentcapture/ContentCaptureSessionCompatTest.java
index f342fa5..3c03635 100644
--- a/core/core/src/androidTest/java/androidx/core/view/contentcapture/ContentCaptureSessionCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/contentcapture/ContentCaptureSessionCompatTest.java
@@ -99,7 +99,8 @@
     }
 
     @Test
-    public void testNotifyViewsAppeared_throwsNPEAboveSDK29() {
+    @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33)
+    public void testNotifyViewsAppeared_throwsNPEBetweenSDK29And33() {
         ContentCaptureSession mockContentCaptureSession = mock(ContentCaptureSession.class);
         ViewStructure mockViewStructure = mock(ViewStructure.class);
         List<ViewStructure> viewStructures = new ArrayList<>();
diff --git a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
index 5ecfc5f..ae70ed1 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
@@ -17,7 +17,6 @@
 package androidx.core.app;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.Manifest;
 import android.app.AppOpsManager;
@@ -222,9 +221,8 @@
                 Context.NOTIFICATION_SERVICE);
     }
 
-    @RestrictTo(TESTS)
     @VisibleForTesting
-    protected NotificationManagerCompat(@NonNull NotificationManager notificationManager,
+    NotificationManagerCompat(@NonNull NotificationManager notificationManager,
             @NonNull Context context) {
         mContext = context;
         mNotificationManager = notificationManager;
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
index b5b9106..0915eb8 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
@@ -38,6 +38,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
 import androidx.core.app.Person;
 import androidx.core.content.LocusIdCompat;
 import androidx.core.graphics.drawable.IconCompat;
@@ -800,7 +801,9 @@
          */
         @NonNull
         public Builder setCategories(@NonNull Set<String> categories) {
-            mInfo.mCategories = categories;
+            ArraySet<String> set = new ArraySet<>();
+            set.addAll(categories);
+            mInfo.mCategories = set;
             return this;
         }
 
diff --git a/core/core/src/main/java/androidx/core/hardware/display/DisplayManagerCompat.java b/core/core/src/main/java/androidx/core/hardware/display/DisplayManagerCompat.java
index 9515111..214c8b5 100644
--- a/core/core/src/main/java/androidx/core/hardware/display/DisplayManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/hardware/display/DisplayManagerCompat.java
@@ -27,6 +27,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
+import java.lang.ref.WeakReference;
 import java.util.WeakHashMap;
 
 /**
@@ -34,7 +35,7 @@
  */
 @SuppressWarnings("unused")
 public final class DisplayManagerCompat {
-    private static final WeakHashMap<Context, DisplayManagerCompat> sInstances =
+    private static final WeakHashMap<Context, WeakReference<DisplayManagerCompat>> sInstances =
             new WeakHashMap<>();
 
     /**
@@ -63,12 +64,12 @@
     @NonNull
     public static DisplayManagerCompat getInstance(@NonNull Context context) {
         synchronized (sInstances) {
-            DisplayManagerCompat instance = sInstances.get(context);
-            if (instance == null) {
-                instance = new DisplayManagerCompat(context);
+            WeakReference<DisplayManagerCompat> instance = sInstances.get(context);
+            if (instance == null || instance.get() == null) {
+                instance = new WeakReference<>(new DisplayManagerCompat(context));
                 sInstances.put(context, instance);
             }
-            return instance;
+            return instance.get();
         }
     }
 
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.java b/core/core/src/main/java/androidx/core/os/BuildCompat.java
index 7bed9ce..4dfca8e 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.java
@@ -27,7 +27,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresOptIn;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 
 import java.util.Locale;
 
@@ -50,7 +50,7 @@
      *
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     protected static boolean isAtLeastPreReleaseCodename(@NonNull String codename,
             @NonNull String buildCodename) {
 
diff --git a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
index a1c9161..4f9487b 100644
--- a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
@@ -18,7 +18,6 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -172,7 +171,6 @@
 
     /** @hide */
     @VisibleForTesting
-    @RestrictTo(TESTS)
     public static void resetTypefaceCache() {
         FontRequestWorker.resetTypefaceCache();
     }
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 7d8bb31..a43c2af 100644
--- a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
@@ -228,7 +228,7 @@
                 insetsController.hide(WindowInsets.Type.ime());
             } else {
                 // Couldn't find an insets controller, fallback to old implementation
-                super.show();
+                super.hide();
             }
         }
     }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
index 594efb9..3524fe2 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
@@ -21,7 +21,6 @@
 import android.os.CancellationSignal
 import android.util.Log
 import androidx.credentials.ClearCredentialStateRequest
-import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.credentials.CreateCredentialRequest
 import androidx.credentials.CreateCredentialResponse
@@ -52,8 +51,7 @@
 @Suppress("deprecation")
 class CredentialProviderPlayServicesImpl(private val context: Context) : CredentialProvider {
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    @set:RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     var googleApiAvailability = GoogleApiAvailability.getInstance()
     override fun onGetCredential(
         request: GetCredentialRequest,
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index 94f8c4b..64039cd 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -134,7 +134,7 @@
             )
             return
         }
-        if (maybeReportErrorResultCodeGet(resultCode, TAG,
+        if (maybeReportErrorResultCodeGet(resultCode,
                 { s, f -> cancelOrCallbackExceptionOrResult(s, f) }, { e ->
                     this.executor.execute {
                         this.callback.onError(e)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
index 2755e88..a7b6746 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -113,7 +113,7 @@
                 "$CONTROLLER_REQUEST_CODE which does not match what was given $uniqueRequestCode")
             return
         }
-        if (maybeReportErrorResultCodeCreate(resultCode, TAG,
+        if (maybeReportErrorResultCodeCreate(resultCode,
                 { s, f -> cancelOrCallbackExceptionOrResult(s, f) }, { e -> this.executor.execute {
                     this.callback.onError(e) } }, cancellationSignal)) return
         val response: CreateCredentialResponse = convertResponseToCredentialManager(Unit)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index 46161e1..ac236eb 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -134,7 +134,7 @@
                 "$CONTROLLER_REQUEST_CODE does not match what was given $uniqueRequestCode")
             return
         }
-        if (maybeReportErrorResultCodeCreate(resultCode, TAG,
+        if (maybeReportErrorResultCodeCreate(resultCode,
                 { s, f -> cancelOrCallbackExceptionOrResult(s, f) }, { e -> this.executor.execute {
                     this.callback.onError(e) } }, cancellationSignal)) return
         val bytes: ByteArray? = data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
index 4278e5f..fafe63a 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
@@ -57,7 +57,6 @@
         @JvmStatic
         protected fun maybeReportErrorResultCodeCreate(
             resultCode: Int,
-            type: String,
             cancelOnError: (
                 CancellationSignal?,
                     () -> Unit
@@ -67,11 +66,11 @@
         ): Boolean {
             if (resultCode != Activity.RESULT_OK) {
                 var exception: CreateCredentialException = CreateCredentialUnknownException(
-                    generateErrorStringUnknown(type, resultCode)
+                    generateErrorStringUnknown(resultCode)
                 )
                 if (resultCode == Activity.RESULT_CANCELED) {
                     exception = CreateCredentialCancellationException(
-                        generateErrorStringCanceled(type)
+                        generateErrorStringCanceled()
                     )
                 }
                 cancelOnError(cancellationSignal) { onError(exception) }
@@ -80,12 +79,12 @@
             return false
         }
 
-        internal fun generateErrorStringUnknown(type: String, resultCode: Int): String {
-            return "$type activity with result code: $resultCode indicating not RESULT_OK"
+        internal fun generateErrorStringUnknown(resultCode: Int): String {
+            return "activity with result code: $resultCode indicating not RESULT_OK"
         }
 
-        internal fun generateErrorStringCanceled(type: String): String {
-            return "$type activity is cancelled by the user."
+        internal fun generateErrorStringCanceled(): String {
+            return "activity is cancelled by the user."
         }
 
         /**
@@ -96,7 +95,6 @@
         @JvmStatic
         protected fun maybeReportErrorResultCodeGet(
             resultCode: Int,
-            type: String,
             cancelOnError: (
                 CancellationSignal?,
                     () -> Unit
@@ -106,11 +104,11 @@
         ): Boolean {
             if (resultCode != Activity.RESULT_OK) {
                 var exception: GetCredentialException = GetCredentialUnknownException(
-                    generateErrorStringUnknown(type, resultCode)
+                    generateErrorStringUnknown(resultCode)
                 )
                 if (resultCode == Activity.RESULT_CANCELED) {
                     exception = GetCredentialCancellationException(
-                        generateErrorStringCanceled(type)
+                        generateErrorStringCanceled()
                     )
                 }
                 cancelOnError(cancellationSignal) { onError(exception) }
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index 34b4020..a9112b0 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -115,10 +115,7 @@
 )
 
 dependencies {
-    bundleInside(project(
-            path: ":datastore:datastore-preferences-proto",
-            configuration: "export"
-    ))
+    bundleInside(project(path: ":datastore:datastore-preferences-proto", configuration: "export"))
 }
 
 androidx {
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 628f11c..5305b10 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -151,3 +151,5 @@
 Failed to compile with Kotlin daemon: java\.lang\.RuntimeException: Could not connect to Kotlin compile daemon
 Using fallback strategy: Compile without Kotlin daemon
 Try \./gradlew \-\-stop if this issue persists\.
+# b/ 279739438
+w\: Detected multiple Kotlin daemon sessions at kotlin/sessions
\ No newline at end of file
diff --git a/development/studio/studio.vmoptions b/development/studio/studio.vmoptions
index 6471f8c..2935f84 100644
--- a/development/studio/studio.vmoptions
+++ b/development/studio/studio.vmoptions
@@ -1,6 +1,6 @@
 -Xmx8g
 -Dappinspection.use.dev.jar=true
--Dlayout.inspector.rel.jar.location=../../../../../../out/androidx/compose/ui/ui-inspection/build/androidx_inspection/assembleInspectorJar/release
+-Dlayout.inspector.rel.jar.location=#studio/../../../../../../out/dist/inspection
 # https://github.com/google/google-java-format#intellij-jre-config
 --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
 --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
diff --git a/development/update_kotlin.sh b/development/update_kotlin.sh
new file mode 100755
index 0000000..a5c3673
--- /dev/null
+++ b/development/update_kotlin.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -e
+
+export KOTLIN_VERSION="1.9.0-dev-6188"
+
+# Download and place konan
+export KONAN_DIR=../../prebuilts/androidx/konan/nativeCompilerPrebuilts/dev/$KOTLIN_VERSION/linux-x86_64
+mkdir -p $KONAN_DIR
+curl -o $KONAN_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz https://download-cf.jetbrains.com/kotlin/native/builds/dev/$KOTLIN_VERSION/linux-x86_64/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz
+
+# Download maven artifacts
+ARTIFACTS_TO_DOWNLOAD="org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlinx-serialization-compiler-plugin-embeddable:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-test:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:$KOTLIN_VERSION"
+
+./development/importMaven/importMaven.sh --allow-jetbrains-dev "$ARTIFACTS_TO_DOWNLOAD"
diff --git a/development/update_studio.sh b/development/update_studio.sh
index 49bc4f7..87535ad 100755
--- a/development/update_studio.sh
+++ b/development/update_studio.sh
@@ -7,8 +7,8 @@
 
 # Get versions
 echo Getting Studio version and link
-AGP_VERSION=${1:-8.1.0-alpha11}
-STUDIO_VERSION_STRING=${2:-"Android Studio Giraffe | 2022.3.1 Canary 11"}
+AGP_VERSION=${1:-8.1.0-beta01}
+STUDIO_VERSION_STRING=${2:-"Android Studio Giraffe | 2022.3.1 Beta 1"}
 STUDIO_IFRAME_LINK=`curl "https://developer.android.com/studio/archive.html" | grep "<iframe " | sed "s/.* src=\"\([^\"]*\)\".*/\1/g"`
 echo iframe link $STUDIO_IFRAME_LINK
 STUDIO_IFRAME_REDIRECT=`curl -s $STUDIO_IFRAME_LINK | grep href | sed 's/.*href="\([^"]*\)".*/\1/g'`
diff --git a/docs/onboarding.md b/docs/onboarding.md
index dc8f072..feb4b7d 100644
--- a/docs/onboarding.md
+++ b/docs/onboarding.md
@@ -785,20 +785,26 @@
 implementing bug fixes are expected to include new regression tests specific to
 the issue being fixed.
 
-### Running Tests
+### Running tests {#run-tests}
 
-#### Single Test Class or Method
+Generally, tests in the AndroidX repository should be run through the Android
+Studio UI. You can also run tests from the command line or via remote devices on
+FTL, see
+[Running unit and integration tests](/company/teams/androidx/testing.md#running)
+for details.
 
-1.  Open the desired test file in Android Studio.
-2.  Right-click on a test class or @Test method name and select `Run FooBarTest`
+#### Single test class or method
 
-#### Full Test Package
+1.  Open the desired test file in Android Studio
+2.  Right-click on a test class or `@Test` method name and select `Run <name>`
 
-1.  In the project side panel open the desired module.
-2.  Find the directory with the tests
-3.  Right-click on the directory and select `Run androidx.foobar`
+#### Full test package
 
-### Running Sample Apps
+1.  In the `Project` side panel, open the desired module
+2.  Find the package directory with the tests
+3.  Right-click on the directory and select `Run <package>`
+
+### Running sample apps {#run-samples}
 
 The AndroidX repository has a set of Android applications that exercise AndroidX
 code. These applications can be useful when you want to debug a real running
diff --git a/emoji2/emoji2-bundled/api/1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
index c6962da..94e4abc 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
@@ -112,7 +112,7 @@
                     .getDimensionPixelSize(R.dimen.emoji_picker_popup_view_elevation)
                     .toFloat()
             showAtLocation(
-                emojiView,
+                popupView,
                 Gravity.NO_GRAVITY,
                 x.roundToInt(),
                 y
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
index d9694c0..d2befe8 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
@@ -28,5 +28,5 @@
     <string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
     <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"沒有可用的 Emoji"</string>
-    <string name="emoji_empty_recent_category" msgid="7863877827879290200">"您尚未使用任何 Emoji"</string>
+    <string name="emoji_empty_recent_category" msgid="7863877827879290200">"你尚未使用任何 Emoji"</string>
 </resources>
diff --git a/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/1.4.0-beta03.txt b/emoji2/emoji2-views/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/res-1.4.0-beta03.txt b/emoji2/emoji2-views/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..8bc8423
--- /dev/null
+++ b/emoji2/emoji2-views/api/res-1.4.0-beta03.txt
@@ -0,0 +1,2 @@
+attr emojiReplaceStrategy
+attr maxEmojiCount
diff --git a/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/1.4.0-beta03.txt b/emoji2/emoji2/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/res-1.4.0-beta03.txt b/emoji2/emoji2/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
index 7de9f8f..18907b9 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
@@ -17,7 +17,6 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.app.Application;
 import android.content.Context;
@@ -43,6 +42,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
@@ -618,7 +618,7 @@
      *
      * @hide
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     @Nullable
     public static EmojiCompat reset(@Nullable final EmojiCompat emojiCompat) {
         synchronized (INSTANCE_LOCK) {
@@ -632,7 +632,7 @@
      *
      * @hide
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     public static void skipDefaultConfigurationLookup(boolean shouldSkip) {
         synchronized (CONFIG_LOCK) {
             sHasDoneDefaultConfigLookup = shouldSkip;
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
index 7c334c6..5c0af43 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
@@ -16,7 +16,6 @@
 package androidx.emoji2.text;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.annotation.SuppressLint;
 import android.graphics.Paint;
@@ -26,6 +25,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.util.Preconditions;
 
 /**
@@ -125,7 +125,7 @@
      *
      * @hide
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     public final int getHeight() {
         return mHeight;
     }
@@ -143,7 +143,7 @@
      *
      * @hide
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     public final int getId() {
         return getTypefaceRasterizer().getId();
     }
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
index f08ae06..35e7fb8 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
@@ -88,7 +88,7 @@
      * @hide
      */
     @NonNull
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     public static MetadataRepo create(@NonNull final Typeface typeface) {
         try {
             TraceCompat.beginSection(S_TRACE_CREATE_REPO);
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
index f120b3c..c505d27 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
@@ -17,7 +17,6 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
 
 import android.annotation.SuppressLint;
 import android.graphics.Canvas;
@@ -30,6 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.emoji2.text.flatbuffer.MetadataItem;
 import androidx.emoji2.text.flatbuffer.MetadataList;
 
@@ -235,7 +235,7 @@
      *
      * @hide
      */
-    @RestrictTo(TESTS)
+    @VisibleForTesting
     public void resetHasGlyphCache() {
         boolean willExclude = isPreferredSystemRender();
         if (willExclude) {
diff --git a/fragment/fragment-ktx/api/1.6.0-beta02.txt b/fragment/fragment-ktx/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public final class FragmentKt {
+    method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+    method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+    method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+    method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+  }
+
+  public final class FragmentManagerKt {
+    method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+  }
+
+  public final class FragmentTransactionKt {
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+  }
+
+  public final class FragmentViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+  public final class ViewKt {
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
+}
+
diff --git a/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public final class FragmentKt {
+    method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+    method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+    method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+    method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+  }
+
+  public final class FragmentManagerKt {
+    method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+  }
+
+  public final class FragmentTransactionKt {
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+  }
+
+  public final class FragmentViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+  public final class ViewKt {
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
+}
+
diff --git a/fragment/fragment-ktx/api/res-1.6.0-beta02.txt b/fragment/fragment-ktx/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-ktx/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt b/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public final class FragmentKt {
+    method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+    method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+    method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+    method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+  }
+
+  public final class FragmentManagerKt {
+    method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+  }
+
+  public final class FragmentTransactionKt {
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+  }
+
+  public final class FragmentViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+  public final class ViewKt {
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
+}
+
diff --git a/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing/api/1.6.0-beta02.txt b/fragment/fragment-testing/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+  public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+    method public void close();
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+    method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+    method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+    method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+    field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+  }
+
+  public static final class FragmentScenario.Companion {
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+  }
+
+  public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+    method public void perform(F fragment);
+  }
+
+  public final class FragmentScenarioKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+  }
+
+}
+
diff --git a/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+  public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+    method public void close();
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+    method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+    method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+    method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+    field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+  }
+
+  public static final class FragmentScenario.Companion {
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+  }
+
+  public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+    method public void perform(F fragment);
+  }
+
+  public final class FragmentScenarioKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+  }
+
+}
+
diff --git a/fragment/fragment-testing/api/res-1.6.0-beta02.txt b/fragment/fragment-testing/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt b/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+  public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+    method public void close();
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+    method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+    method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+    method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+    field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+  }
+
+  public static final class FragmentScenario.Companion {
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+    method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+  }
+
+  public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+    method public void perform(F fragment);
+  }
+
+  public final class FragmentScenarioKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+  }
+
+}
+
diff --git a/fragment/fragment/api/1.6.0-beta02.txt b/fragment/fragment/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..4d44df2
--- /dev/null
+++ b/fragment/fragment/api/1.6.0-beta02.txt
@@ -0,0 +1,554 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    ctor public DialogFragment(@LayoutRes int);
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method @MainThread public void dismissNow();
+    method public android.app.Dialog? getDialog();
+    method public boolean getShowsDialog();
+    method @StyleRes public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+    method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
+    method public final android.app.Dialog requireDialog();
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, @StyleRes int);
+    method public void show(androidx.fragment.app.FragmentManager, String?);
+    method public int show(androidx.fragment.app.FragmentTransaction, String?);
+    method public void showNow(androidx.fragment.app.FragmentManager, String?);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+    ctor public Fragment();
+    ctor @ContentView public Fragment(@LayoutRes int);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public final boolean equals(Object?);
+    method public final androidx.fragment.app.FragmentActivity? getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle? getArguments();
+    method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public Object? getEnterTransition();
+    method public Object? getExitTransition();
+    method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+    method public final Object? getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+    method public final androidx.fragment.app.Fragment? getParentFragment();
+    method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+    method public Object? getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method @Deprecated public final boolean getRetainInstance();
+    method public Object? getReturnTransition();
+    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public Object? getSharedElementEnterTransition();
+    method public Object? getSharedElementReturnTransition();
+    method public final String getString(@StringRes int);
+    method public final String getString(@StringRes int, java.lang.Object!...);
+    method public final String? getTag();
+    method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+    method @Deprecated public final int getTargetRequestCode();
+    method public final CharSequence getText(@StringRes int);
+    method @Deprecated public boolean getUserVisibleHint();
+    method public android.view.View? getView();
+    method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+    method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    method public final int hashCode();
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+    method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+    method @CallSuper @MainThread public void onAttach(android.content.Context);
+    method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+    method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+    method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+    method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+    method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+    method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+    method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+    method @CallSuper @MainThread public void onDestroy();
+    method @Deprecated @MainThread public void onDestroyOptionsMenu();
+    method @CallSuper @MainThread public void onDestroyView();
+    method @CallSuper @MainThread public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+    method @MainThread public void onHiddenChanged(boolean);
+    method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+    method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+    method @CallSuper @MainThread public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+    method @CallSuper @MainThread public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+    method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+    method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+    method @CallSuper @MainThread public void onResume();
+    method @MainThread public void onSaveInstanceState(android.os.Bundle);
+    method @CallSuper @MainThread public void onStart();
+    method @CallSuper @MainThread public void onStop();
+    method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+    method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+    method public void postponeEnterTransition();
+    method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+    method public void registerForContextMenu(android.view.View);
+    method @Deprecated public final void requestPermissions(String![], int);
+    method public final androidx.fragment.app.FragmentActivity requireActivity();
+    method public final android.os.Bundle requireArguments();
+    method public final android.content.Context requireContext();
+    method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+    method public final Object requireHost();
+    method public final androidx.fragment.app.Fragment requireParentFragment();
+    method public final android.view.View requireView();
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle?);
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setEnterTransition(Object?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitTransition(Object?);
+    method @Deprecated public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(Object?);
+    method @Deprecated public void setRetainInstance(boolean);
+    method public void setReturnTransition(Object?);
+    method public void setSharedElementEnterTransition(Object?);
+    method public void setSharedElementReturnTransition(Object?);
+    method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+    method @Deprecated public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle?);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(String, Exception?);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+  }
+
+  public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.lifecycle.LifecycleOwner {
+    ctor public FragmentActivity();
+    ctor @ContentView public FragmentActivity(@LayoutRes int);
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method protected void onResumeFragments();
+    method public void onStateNotSaved();
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method @Deprecated public void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public abstract android.view.View? onFindViewById(@IdRes int);
+    method public abstract boolean onHasView();
+  }
+
+  public final class FragmentContainerView extends android.widget.FrameLayout {
+    ctor public FragmentContainerView(android.content.Context context);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+    method public <F extends androidx.fragment.app.Fragment> F getFragment();
+  }
+
+  public class FragmentController {
+    method public void attachHost(androidx.fragment.app.Fragment?);
+    method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+    method public void dispatchActivityCreated();
+    method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method @Deprecated public void dispatchLowMemory();
+    method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+    method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+    method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method @Deprecated public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method @Deprecated public void doLoaderDestroy();
+    method @Deprecated public void doLoaderRetain();
+    method @Deprecated public void doLoaderStart();
+    method @Deprecated public void doLoaderStop(boolean);
+    method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public boolean execPendingActions();
+    method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+    method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+    method public int getActiveFragmentsCount();
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+    method @Deprecated public void reportLoaderStart();
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+    method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+    method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+    method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+    method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+    method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+    method @Deprecated public android.os.Parcelable? saveAllState();
+  }
+
+  public class FragmentFactory {
+    ctor public FragmentFactory();
+    method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+    method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+  }
+
+  public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public android.view.View? onFindViewById(int);
+    method public abstract E? onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+    method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(String);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+    ctor public FragmentManager();
+    method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public androidx.fragment.app.FragmentTransaction beginTransaction();
+    method public void clearBackStack(String);
+    method public final void clearFragmentResult(String);
+    method public final void clearFragmentResultListener(String);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method @Deprecated public static void enableDebugLogging(boolean);
+    method @MainThread public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+    method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+    method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+    method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public int getBackStackEntryCount();
+    method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+    method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+    method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+    method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+    method public boolean isDestroyed();
+    method public boolean isStateSaved();
+    method public void popBackStack();
+    method public void popBackStack(String?, int);
+    method public void popBackStack(int, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
+    method public boolean popBackStackImmediate(int, int);
+    method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+    method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public void restoreBackStack(String);
+    method public void saveBackStack(String);
+    method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+    method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+    method public final void setFragmentResult(String, android.os.Bundle);
+    method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+    method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+    method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static interface FragmentManager.BackStackEntry {
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+    method public int getId();
+    method public String? getName();
+  }
+
+  public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+    method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  public static interface FragmentManager.OnBackStackChangedListener {
+    method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public void onBackStackChanged();
+  }
+
+  @Deprecated public class FragmentManagerNonConfig {
+  }
+
+  public interface FragmentOnAttachListener {
+    method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public long getItemId(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  public interface FragmentResultListener {
+    method public void onFragmentResult(String, android.os.Bundle);
+  }
+
+  public interface FragmentResultOwner {
+    method public void clearFragmentResult(String);
+    method public void clearFragmentResultListener(String);
+    method public void setFragmentResult(String, android.os.Bundle);
+    method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+  }
+
+  @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor @Deprecated public FragmentTabHost(android.content.Context);
+    ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+    method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+    method @Deprecated public void onTabChanged(String?);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor @Deprecated public FragmentTransaction();
+    method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+    method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+    method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
+    method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+    method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+    method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+    method public boolean isAddToBackStackAllowed();
+    method public boolean isEmpty();
+    method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+    method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+    method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public androidx.fragment.app.FragmentTransaction setTransition(int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+    method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class ListFragment extends androidx.fragment.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter? getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public final android.widget.ListAdapter requireListAdapter();
+    method public void setEmptyText(CharSequence?);
+    method public void setListAdapter(android.widget.ListAdapter?);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+  public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+    method public String getPreviousFragmentId();
+    property public final String previousFragmentId;
+  }
+
+  public final class FragmentStrictMode {
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+    method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+    method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+    property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+  }
+
+  public static fun interface FragmentStrictMode.OnViolationListener {
+    method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+  }
+
+  public static final class FragmentStrictMode.Policy {
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+  }
+
+  public static final class FragmentStrictMode.Policy.Builder {
+    ctor public FragmentStrictMode.Policy.Builder();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+  }
+
+  public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup? getParentContainer();
+    property public final android.view.ViewGroup? parentContainer;
+  }
+
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
+    property public final int requestCode;
+    property public final androidx.fragment.app.Fragment targetFragment;
+  }
+
+  public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
+    property public final boolean isVisibleToUser;
+  }
+
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.RuntimeException {
+    method public final androidx.fragment.app.Fragment getFragment();
+    property public final androidx.fragment.app.Fragment fragment;
+  }
+
+  public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
+    property public final android.view.ViewGroup container;
+  }
+
+  public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+    method public int getContainerId();
+    method public androidx.fragment.app.Fragment getExpectedParentFragment();
+    property public final int containerId;
+    property public final androidx.fragment.app.Fragment expectedParentFragment;
+  }
+
+}
+
diff --git a/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..4d44df2
--- /dev/null
+++ b/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,554 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    ctor public DialogFragment(@LayoutRes int);
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method @MainThread public void dismissNow();
+    method public android.app.Dialog? getDialog();
+    method public boolean getShowsDialog();
+    method @StyleRes public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+    method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
+    method public final android.app.Dialog requireDialog();
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, @StyleRes int);
+    method public void show(androidx.fragment.app.FragmentManager, String?);
+    method public int show(androidx.fragment.app.FragmentTransaction, String?);
+    method public void showNow(androidx.fragment.app.FragmentManager, String?);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+    ctor public Fragment();
+    ctor @ContentView public Fragment(@LayoutRes int);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public final boolean equals(Object?);
+    method public final androidx.fragment.app.FragmentActivity? getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle? getArguments();
+    method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public Object? getEnterTransition();
+    method public Object? getExitTransition();
+    method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+    method public final Object? getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+    method public final androidx.fragment.app.Fragment? getParentFragment();
+    method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+    method public Object? getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method @Deprecated public final boolean getRetainInstance();
+    method public Object? getReturnTransition();
+    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public Object? getSharedElementEnterTransition();
+    method public Object? getSharedElementReturnTransition();
+    method public final String getString(@StringRes int);
+    method public final String getString(@StringRes int, java.lang.Object!...);
+    method public final String? getTag();
+    method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+    method @Deprecated public final int getTargetRequestCode();
+    method public final CharSequence getText(@StringRes int);
+    method @Deprecated public boolean getUserVisibleHint();
+    method public android.view.View? getView();
+    method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+    method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    method public final int hashCode();
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+    method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+    method @CallSuper @MainThread public void onAttach(android.content.Context);
+    method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+    method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+    method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+    method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+    method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+    method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+    method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+    method @CallSuper @MainThread public void onDestroy();
+    method @Deprecated @MainThread public void onDestroyOptionsMenu();
+    method @CallSuper @MainThread public void onDestroyView();
+    method @CallSuper @MainThread public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+    method @MainThread public void onHiddenChanged(boolean);
+    method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+    method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+    method @CallSuper @MainThread public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+    method @CallSuper @MainThread public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+    method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+    method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+    method @CallSuper @MainThread public void onResume();
+    method @MainThread public void onSaveInstanceState(android.os.Bundle);
+    method @CallSuper @MainThread public void onStart();
+    method @CallSuper @MainThread public void onStop();
+    method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+    method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+    method public void postponeEnterTransition();
+    method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+    method public void registerForContextMenu(android.view.View);
+    method @Deprecated public final void requestPermissions(String![], int);
+    method public final androidx.fragment.app.FragmentActivity requireActivity();
+    method public final android.os.Bundle requireArguments();
+    method public final android.content.Context requireContext();
+    method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+    method public final Object requireHost();
+    method public final androidx.fragment.app.Fragment requireParentFragment();
+    method public final android.view.View requireView();
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle?);
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setEnterTransition(Object?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitTransition(Object?);
+    method @Deprecated public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(Object?);
+    method @Deprecated public void setRetainInstance(boolean);
+    method public void setReturnTransition(Object?);
+    method public void setSharedElementEnterTransition(Object?);
+    method public void setSharedElementReturnTransition(Object?);
+    method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+    method @Deprecated public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle?);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(String, Exception?);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+  }
+
+  public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.lifecycle.LifecycleOwner {
+    ctor public FragmentActivity();
+    ctor @ContentView public FragmentActivity(@LayoutRes int);
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method protected void onResumeFragments();
+    method public void onStateNotSaved();
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method @Deprecated public void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public abstract android.view.View? onFindViewById(@IdRes int);
+    method public abstract boolean onHasView();
+  }
+
+  public final class FragmentContainerView extends android.widget.FrameLayout {
+    ctor public FragmentContainerView(android.content.Context context);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+    method public <F extends androidx.fragment.app.Fragment> F getFragment();
+  }
+
+  public class FragmentController {
+    method public void attachHost(androidx.fragment.app.Fragment?);
+    method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+    method public void dispatchActivityCreated();
+    method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method @Deprecated public void dispatchLowMemory();
+    method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+    method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+    method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method @Deprecated public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method @Deprecated public void doLoaderDestroy();
+    method @Deprecated public void doLoaderRetain();
+    method @Deprecated public void doLoaderStart();
+    method @Deprecated public void doLoaderStop(boolean);
+    method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public boolean execPendingActions();
+    method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+    method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+    method public int getActiveFragmentsCount();
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+    method @Deprecated public void reportLoaderStart();
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+    method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+    method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+    method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+    method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+    method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+    method @Deprecated public android.os.Parcelable? saveAllState();
+  }
+
+  public class FragmentFactory {
+    ctor public FragmentFactory();
+    method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+    method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+  }
+
+  public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public android.view.View? onFindViewById(int);
+    method public abstract E? onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+    method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(String);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+    ctor public FragmentManager();
+    method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public androidx.fragment.app.FragmentTransaction beginTransaction();
+    method public void clearBackStack(String);
+    method public final void clearFragmentResult(String);
+    method public final void clearFragmentResultListener(String);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method @Deprecated public static void enableDebugLogging(boolean);
+    method @MainThread public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+    method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+    method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+    method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public int getBackStackEntryCount();
+    method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+    method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+    method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+    method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+    method public boolean isDestroyed();
+    method public boolean isStateSaved();
+    method public void popBackStack();
+    method public void popBackStack(String?, int);
+    method public void popBackStack(int, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
+    method public boolean popBackStackImmediate(int, int);
+    method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+    method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public void restoreBackStack(String);
+    method public void saveBackStack(String);
+    method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+    method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+    method public final void setFragmentResult(String, android.os.Bundle);
+    method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+    method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+    method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static interface FragmentManager.BackStackEntry {
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+    method public int getId();
+    method public String? getName();
+  }
+
+  public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+    method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  public static interface FragmentManager.OnBackStackChangedListener {
+    method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public void onBackStackChanged();
+  }
+
+  @Deprecated public class FragmentManagerNonConfig {
+  }
+
+  public interface FragmentOnAttachListener {
+    method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public long getItemId(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  public interface FragmentResultListener {
+    method public void onFragmentResult(String, android.os.Bundle);
+  }
+
+  public interface FragmentResultOwner {
+    method public void clearFragmentResult(String);
+    method public void clearFragmentResultListener(String);
+    method public void setFragmentResult(String, android.os.Bundle);
+    method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+  }
+
+  @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor @Deprecated public FragmentTabHost(android.content.Context);
+    ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+    method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+    method @Deprecated public void onTabChanged(String?);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor @Deprecated public FragmentTransaction();
+    method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+    method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+    method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
+    method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+    method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+    method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+    method public boolean isAddToBackStackAllowed();
+    method public boolean isEmpty();
+    method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+    method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+    method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public androidx.fragment.app.FragmentTransaction setTransition(int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+    method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class ListFragment extends androidx.fragment.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter? getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public final android.widget.ListAdapter requireListAdapter();
+    method public void setEmptyText(CharSequence?);
+    method public void setListAdapter(android.widget.ListAdapter?);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+  public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+    method public String getPreviousFragmentId();
+    property public final String previousFragmentId;
+  }
+
+  public final class FragmentStrictMode {
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+    method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+    method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+    property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+  }
+
+  public static fun interface FragmentStrictMode.OnViolationListener {
+    method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+  }
+
+  public static final class FragmentStrictMode.Policy {
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+  }
+
+  public static final class FragmentStrictMode.Policy.Builder {
+    ctor public FragmentStrictMode.Policy.Builder();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+  }
+
+  public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup? getParentContainer();
+    property public final android.view.ViewGroup? parentContainer;
+  }
+
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
+    property public final int requestCode;
+    property public final androidx.fragment.app.Fragment targetFragment;
+  }
+
+  public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
+    property public final boolean isVisibleToUser;
+  }
+
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.RuntimeException {
+    method public final androidx.fragment.app.Fragment getFragment();
+    property public final androidx.fragment.app.Fragment fragment;
+  }
+
+  public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
+    property public final android.view.ViewGroup container;
+  }
+
+  public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+    method public int getContainerId();
+    method public androidx.fragment.app.Fragment getExpectedParentFragment();
+    property public final int containerId;
+    property public final androidx.fragment.app.Fragment expectedParentFragment;
+  }
+
+}
+
diff --git a/fragment/fragment/api/res-1.6.0-beta02.txt b/fragment/fragment/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment/api/restricted_1.6.0-beta02.txt b/fragment/fragment/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..369fe63
--- /dev/null
+++ b/fragment/fragment/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,583 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+  public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    ctor public DialogFragment(@LayoutRes int);
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method @MainThread public void dismissNow();
+    method public android.app.Dialog? getDialog();
+    method public boolean getShowsDialog();
+    method @StyleRes public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+    method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
+    method public final android.app.Dialog requireDialog();
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, @StyleRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setupDialog(android.app.Dialog, int);
+    method public void show(androidx.fragment.app.FragmentManager, String?);
+    method public int show(androidx.fragment.app.FragmentTransaction, String?);
+    method public void showNow(androidx.fragment.app.FragmentManager, String?);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+    ctor public Fragment();
+    ctor @ContentView public Fragment(@LayoutRes int);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public final boolean equals(Object?);
+    method public final androidx.fragment.app.FragmentActivity? getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle? getArguments();
+    method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public Object? getEnterTransition();
+    method public Object? getExitTransition();
+    method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+    method public final Object? getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.view.LayoutInflater getLayoutInflater(android.os.Bundle?);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+    method public final androidx.fragment.app.Fragment? getParentFragment();
+    method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+    method public Object? getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method @Deprecated public final boolean getRetainInstance();
+    method public Object? getReturnTransition();
+    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public Object? getSharedElementEnterTransition();
+    method public Object? getSharedElementReturnTransition();
+    method public final String getString(@StringRes int);
+    method public final String getString(@StringRes int, java.lang.Object!...);
+    method public final String? getTag();
+    method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+    method @Deprecated public final int getTargetRequestCode();
+    method public final CharSequence getText(@StringRes int);
+    method @Deprecated public boolean getUserVisibleHint();
+    method public android.view.View? getView();
+    method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+    method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final boolean hasOptionsMenu();
+    method public final int hashCode();
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+    method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final boolean isMenuVisible();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+    method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+    method @CallSuper @MainThread public void onAttach(android.content.Context);
+    method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+    method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+    method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+    method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+    method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+    method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+    method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+    method @CallSuper @MainThread public void onDestroy();
+    method @Deprecated @MainThread public void onDestroyOptionsMenu();
+    method @CallSuper @MainThread public void onDestroyView();
+    method @CallSuper @MainThread public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+    method @MainThread public void onHiddenChanged(boolean);
+    method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+    method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+    method @CallSuper @MainThread public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+    method @CallSuper @MainThread public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+    method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+    method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+    method @CallSuper @MainThread public void onResume();
+    method @MainThread public void onSaveInstanceState(android.os.Bundle);
+    method @CallSuper @MainThread public void onStart();
+    method @CallSuper @MainThread public void onStop();
+    method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+    method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+    method public void postponeEnterTransition();
+    method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+    method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+    method public void registerForContextMenu(android.view.View);
+    method @Deprecated public final void requestPermissions(String![], int);
+    method public final androidx.fragment.app.FragmentActivity requireActivity();
+    method public final android.os.Bundle requireArguments();
+    method public final android.content.Context requireContext();
+    method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+    method public final Object requireHost();
+    method public final androidx.fragment.app.Fragment requireParentFragment();
+    method public final android.view.View requireView();
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle?);
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setEnterTransition(Object?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitTransition(Object?);
+    method @Deprecated public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(Object?);
+    method @Deprecated public void setRetainInstance(boolean);
+    method public void setReturnTransition(Object?);
+    method public void setSharedElementEnterTransition(Object?);
+    method public void setSharedElementReturnTransition(Object?);
+    method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+    method @Deprecated public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle?);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int);
+    method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(String, Exception?);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+  }
+
+  public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.core.app.ActivityCompat.RequestPermissionsRequestCodeValidator {
+    ctor public FragmentActivity();
+    ctor @ContentView public FragmentActivity(@LayoutRes int);
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+    method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+    method protected void onResumeFragments();
+    method public void onStateNotSaved();
+    method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method @Deprecated public void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+    method public abstract android.view.View? onFindViewById(@IdRes int);
+    method public abstract boolean onHasView();
+  }
+
+  public final class FragmentContainerView extends android.widget.FrameLayout {
+    ctor public FragmentContainerView(android.content.Context context);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+    method public <F extends androidx.fragment.app.Fragment> F getFragment();
+  }
+
+  public class FragmentController {
+    method public void attachHost(androidx.fragment.app.Fragment?);
+    method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+    method public void dispatchActivityCreated();
+    method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method @Deprecated public void dispatchLowMemory();
+    method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+    method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+    method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method @Deprecated public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method @Deprecated public void doLoaderDestroy();
+    method @Deprecated public void doLoaderRetain();
+    method @Deprecated public void doLoaderStart();
+    method @Deprecated public void doLoaderStop(boolean);
+    method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public boolean execPendingActions();
+    method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+    method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+    method public int getActiveFragmentsCount();
+    method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+    method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+    method @Deprecated public void reportLoaderStart();
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+    method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+    method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+    method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+    method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+    method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+    method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+    method @Deprecated public android.os.Parcelable? saveAllState();
+  }
+
+  public class FragmentFactory {
+    ctor public FragmentFactory();
+    method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+    method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+  }
+
+  public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method public android.view.View? onFindViewById(int);
+    method public abstract E? onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+    method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(String);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+    method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+    ctor public FragmentManager();
+    method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public androidx.fragment.app.FragmentTransaction beginTransaction();
+    method public void clearBackStack(String);
+    method public final void clearFragmentResult(String);
+    method public final void clearFragmentResultListener(String);
+    method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+    method @Deprecated public static void enableDebugLogging(boolean);
+    method @MainThread public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+    method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+    method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+    method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public int getBackStackEntryCount();
+    method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+    method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+    method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+    method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+    method public boolean isDestroyed();
+    method public boolean isStateSaved();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.fragment.app.FragmentTransaction openTransaction();
+    method public void popBackStack();
+    method public void popBackStack(String?, int);
+    method public void popBackStack(int, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
+    method public boolean popBackStackImmediate(int, int);
+    method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+    method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+    method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+    method public void restoreBackStack(String);
+    method public void saveBackStack(String);
+    method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+    method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+    method public final void setFragmentResult(String, android.os.Bundle);
+    method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+    method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+    method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static interface FragmentManager.BackStackEntry {
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+    method public int getId();
+    method public String? getName();
+  }
+
+  public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+    method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+    method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+    method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  public static interface FragmentManager.OnBackStackChangedListener {
+    method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public void onBackStackChanged();
+  }
+
+  @Deprecated public class FragmentManagerNonConfig {
+  }
+
+  public interface FragmentOnAttachListener {
+    method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+  }
+
+  @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public long getItemId(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  public interface FragmentResultListener {
+    method public void onFragmentResult(String, android.os.Bundle);
+  }
+
+  public interface FragmentResultOwner {
+    method public void clearFragmentResult(String);
+    method public void clearFragmentResultListener(String);
+    method public void setFragmentResult(String, android.os.Bundle);
+    method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+  }
+
+  @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+    ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+    method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+    method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+    field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+  }
+
+  @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor @Deprecated public FragmentTabHost(android.content.Context);
+    ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+    method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+    method @Deprecated public void onTabChanged(String?);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+    method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor @Deprecated public FragmentTransaction();
+    method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+    method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+    method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
+    method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+    method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+    method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+    method public boolean isAddToBackStackAllowed();
+    method public boolean isEmpty();
+    method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+    method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+    method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+    method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+    method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+    method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+    method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public androidx.fragment.app.FragmentTransaction setTransition(int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+    method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+    field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class FragmentTransitionImpl {
+    ctor public FragmentTransitionImpl();
+    method public abstract void addTarget(Object, android.view.View);
+    method public abstract void addTargets(Object, java.util.ArrayList<android.view.View!>);
+    method public abstract void beginDelayedTransition(android.view.ViewGroup, Object?);
+    method protected static void bfsAddViewChildren(java.util.List<android.view.View!>!, android.view.View!);
+    method public abstract boolean canHandle(Object);
+    method public abstract Object! cloneTransition(Object?);
+    method protected void getBoundsOnScreen(android.view.View!, android.graphics.Rect!);
+    method protected static boolean isNullOrEmpty(java.util.List!);
+    method public abstract Object! mergeTransitionsInSequence(Object?, Object?, Object?);
+    method public abstract Object! mergeTransitionsTogether(Object?, Object?, Object?);
+    method public abstract void removeTarget(Object, android.view.View);
+    method public abstract void replaceTargets(Object, java.util.ArrayList<android.view.View!>!, java.util.ArrayList<android.view.View!>!);
+    method public abstract void scheduleHideFragmentView(Object, android.view.View, java.util.ArrayList<android.view.View!>);
+    method public abstract void scheduleRemoveTargets(Object, Object?, java.util.ArrayList<android.view.View!>?, Object?, java.util.ArrayList<android.view.View!>?, Object?, java.util.ArrayList<android.view.View!>?);
+    method public abstract void setEpicenter(Object, android.view.View?);
+    method public abstract void setEpicenter(Object, android.graphics.Rect);
+    method public void setListenerForTransitionEnd(androidx.fragment.app.Fragment, Object, androidx.core.os.CancellationSignal, Runnable);
+    method public abstract void setSharedElementTargets(Object, android.view.View, java.util.ArrayList<android.view.View!>);
+    method public abstract void swapSharedElementTargets(Object?, java.util.ArrayList<android.view.View!>?, java.util.ArrayList<android.view.View!>?);
+    method public abstract Object! wrapTransitionInSet(Object?);
+  }
+
+  public class ListFragment extends androidx.fragment.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter? getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public final android.widget.ListAdapter requireListAdapter();
+    method public void setEmptyText(CharSequence?);
+    method public void setListAdapter(android.widget.ListAdapter?);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+  public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+    method public String getPreviousFragmentId();
+    property public final String previousFragmentId;
+  }
+
+  public final class FragmentStrictMode {
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+    method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+    method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+    property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+  }
+
+  public static fun interface FragmentStrictMode.OnViolationListener {
+    method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+  }
+
+  public static final class FragmentStrictMode.Policy {
+    field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+  }
+
+  public static final class FragmentStrictMode.Policy.Builder {
+    ctor public FragmentStrictMode.Policy.Builder();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+    method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+  }
+
+  public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup? getParentContainer();
+    property public final android.view.ViewGroup? parentContainer;
+  }
+
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
+    property public final int requestCode;
+    property public final androidx.fragment.app.Fragment targetFragment;
+  }
+
+  public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
+    property public final boolean isVisibleToUser;
+  }
+
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.RuntimeException {
+    method public final androidx.fragment.app.Fragment getFragment();
+    property public final androidx.fragment.app.Fragment fragment;
+  }
+
+  public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
+    property public final android.view.ViewGroup container;
+  }
+
+  public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+    method public int getContainerId();
+    method public androidx.fragment.app.Fragment getExpectedParentFragment();
+    property public final int containerId;
+    property public final androidx.fragment.app.Fragment expectedParentFragment;
+  }
+
+}
+
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
index 02fa8c0..f331268 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
@@ -404,4 +404,45 @@
             assertThat(committedCount).isEqualTo(0)
         }
     }
+
+    @Test
+    fun testOnBackChangeNoAddToBackstackWithAddToBackStack() {
+        with(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fragmentManager = withActivity { supportFragmentManager }
+
+            val fragment = StrictFragment()
+            val fragment2 = StrictFragment()
+            var startedCount = 0
+            var committedCount = 0
+            val listener = object : OnBackStackChangedListener {
+                override fun onBackStackChanged() { /* nothing */ }
+
+                override fun onBackStackChangeStarted(fragment: Fragment, pop: Boolean) {
+                    startedCount++
+                }
+
+                override fun onBackStackChangeCommitted(fragment: Fragment, pop: Boolean) {
+                    committedCount++
+                }
+            }
+            fragmentManager.addOnBackStackChangedListener(listener)
+
+            withActivity {
+                fragmentManager.beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(R.id.content, fragment)
+                    .commit()
+
+                fragmentManager.beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(R.id.content, fragment2)
+                    .addToBackStack(null)
+                    .commit()
+                executePendingTransactions()
+            }
+
+            assertThat(startedCount).isEqualTo(1)
+            assertThat(committedCount).isEqualTo(1)
+        }
+    }
 }
\ No newline at end of file
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index dae7831..01cd33a 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -2139,7 +2139,7 @@
         Set<Fragment> fragments = new HashSet<>();
         for (int i = 0; i < record.mOps.size(); i++) {
             Fragment f = record.mOps.get(i).mFragment;
-            if (f != null) {
+            if (f != null && record.mAddToBackStack) {
                 fragments.add(f);
             }
         }
diff --git a/glance/glance-appwidget-preview/api/1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/build.gradle b/glance/glance-appwidget-preview/build.gradle
index bb9faa2..07567f5 100644
--- a/glance/glance-appwidget-preview/build.gradle
+++ b/glance/glance-appwidget-preview/build.gradle
@@ -35,7 +35,7 @@
 
     implementation("androidx.core:core:1.1.0")
 
-    api(project(":compose:runtime:runtime"))
+    api("androidx.compose.runtime:runtime:1.2.1")
     api(project(":glance:glance"))
     api(project(":glance:glance-appwidget"))
 
@@ -54,6 +54,7 @@
 androidx {
     name = "Android Glance AppWidget Preview"
     type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.GLANCE_PREVIEW
     inceptionYear = "2022"
     description = "Glance tooling library. This library provides the API required for the " +
             "GlanceAppWidget components and its Glance @Composable to be previewable in the IDE."
diff --git a/glance/glance-appwidget/api/1.0.0-beta01.txt b/glance/glance-appwidget/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..72717d0
--- /dev/null
+++ b/glance/glance-appwidget/api/1.0.0-beta01.txt
@@ -0,0 +1,264 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+  public final class AndroidRemoteViewsKt {
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class AppWidgetBackgroundKt {
+    method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+  }
+
+  public final class AppWidgetComposerKt {
+    method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+  }
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+  }
+
+  public abstract sealed class CheckBoxColors {
+  }
+
+  public final class CheckBoxKt {
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+    field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CircularProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+  }
+
+  public final class CompositionLocalsKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+  }
+
+  public final class CornerRadiusKt {
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+  }
+
+  public abstract class GlanceAppWidget {
+    ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+    method public androidx.glance.appwidget.SizeMode getSizeMode();
+    method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+    method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public androidx.glance.appwidget.SizeMode sizeMode;
+    property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+  }
+
+  public final class GlanceAppWidgetKt {
+    method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+    method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+  }
+
+  public final class GlanceAppWidgetManager {
+    ctor public GlanceAppWidgetManager(android.content.Context context);
+    method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+    method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+    method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+    method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+  }
+
+  public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+    ctor public GlanceAppWidgetReceiver();
+    method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+    property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+    field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+    field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+  }
+
+  public static final class GlanceAppWidgetReceiver.Companion {
+  }
+
+  public final class ImageProvidersKt {
+    method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+  }
+
+  public final class LinearProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+    method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+    property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+    property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+    field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+    field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+  }
+
+  public sealed interface SizeMode {
+  }
+
+  public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+  }
+
+  public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+    ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+    method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+    property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+  }
+
+  public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+  }
+
+  public abstract sealed class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+    field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+  }
+
+}
+
+package androidx.glance.appwidget.action {
+
+  public interface ActionCallback {
+    method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class RunCallbackActionKt {
+    method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class SendBroadcastActionKt {
+    method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+    method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+    method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+  }
+
+  public final class StartActivityIntentActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class StartServiceActionKt {
+    method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+    method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+    method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+    method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+  }
+
+  public final class ToggleableKt {
+    method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+    property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+  }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+  public abstract sealed class GridCells {
+  }
+
+  @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Adaptive(float minSize);
+    method public float getMinSize();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+  }
+
+  public final class LazyListKt {
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyListScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  @kotlin.DslMarker public @interface LazyScopeMarker {
+  }
+
+  public final class LazyVerticalGridKt {
+    method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyVerticalGridScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+}
+
+package androidx.glance.appwidget.state {
+
+  public final class GlanceAppWidgetStateKt {
+    method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+}
+
diff --git a/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..4ac3223
--- /dev/null
+++ b/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,278 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+  public final class AndroidRemoteViewsKt {
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class AppWidgetBackgroundKt {
+    method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+  }
+
+  public final class AppWidgetComposerKt {
+    method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+  }
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+  }
+
+  public abstract sealed class CheckBoxColors {
+  }
+
+  public final class CheckBoxKt {
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+    field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CircularProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+  }
+
+  public final class CompositionLocalsKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+  }
+
+  public final class CornerRadiusKt {
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+  }
+
+  @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlanceRemoteViewsApi {
+  }
+
+  public abstract class GlanceAppWidget {
+    ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+    method public androidx.glance.appwidget.SizeMode getSizeMode();
+    method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+    method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public androidx.glance.appwidget.SizeMode sizeMode;
+    property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+  }
+
+  public final class GlanceAppWidgetKt {
+    method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+    method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+  }
+
+  public final class GlanceAppWidgetManager {
+    ctor public GlanceAppWidgetManager(android.content.Context context);
+    method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+    method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+    method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+    method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+  }
+
+  public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+    ctor public GlanceAppWidgetReceiver();
+    method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+    property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+    field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+    field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+  }
+
+  public static final class GlanceAppWidgetReceiver.Companion {
+  }
+
+  @androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi public final class GlanceRemoteViews {
+    ctor public GlanceRemoteViews();
+    method public suspend Object? compose(android.content.Context context, long size, optional Object? state, optional android.os.Bundle appWidgetOptions, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<? super androidx.glance.appwidget.RemoteViewsCompositionResult>);
+  }
+
+  public final class ImageProvidersKt {
+    method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+  }
+
+  public final class LinearProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+    method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+    property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+    property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+    field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+    field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+  }
+
+  @androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi public final class RemoteViewsCompositionResult {
+    ctor public RemoteViewsCompositionResult(android.widget.RemoteViews remoteViews);
+    method public android.widget.RemoteViews getRemoteViews();
+    property public final android.widget.RemoteViews remoteViews;
+  }
+
+  public sealed interface SizeMode {
+  }
+
+  public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+  }
+
+  public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+    ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+    method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+    property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+  }
+
+  public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+  }
+
+  public abstract sealed class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+    field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+  }
+
+}
+
+package androidx.glance.appwidget.action {
+
+  public interface ActionCallback {
+    method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class RunCallbackActionKt {
+    method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class SendBroadcastActionKt {
+    method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+    method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+    method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+  }
+
+  public final class StartActivityIntentActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class StartServiceActionKt {
+    method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+    method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+    method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+    method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+  }
+
+  public final class ToggleableKt {
+    method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+    property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+  }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+  public abstract sealed class GridCells {
+  }
+
+  @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Adaptive(float minSize);
+    method public float getMinSize();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+  }
+
+  public final class LazyListKt {
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyListScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  @kotlin.DslMarker public @interface LazyScopeMarker {
+  }
+
+  public final class LazyVerticalGridKt {
+    method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyVerticalGridScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+}
+
+package androidx.glance.appwidget.state {
+
+  public final class GlanceAppWidgetStateKt {
+    method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+}
+
diff --git a/glance/glance-appwidget/api/res-1.0.0-beta01.txt b/glance/glance-appwidget/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..dba4906
--- /dev/null
+++ b/glance/glance-appwidget/api/res-1.0.0-beta01.txt
@@ -0,0 +1,2 @@
+bool glance_appwidget_available
+layout glance_default_loading_layout
diff --git a/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt b/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..72717d0
--- /dev/null
+++ b/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,264 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+  public final class AndroidRemoteViewsKt {
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class AppWidgetBackgroundKt {
+    method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+  }
+
+  public final class AppWidgetComposerKt {
+    method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+  }
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+  }
+
+  public abstract sealed class CheckBoxColors {
+  }
+
+  public final class CheckBoxKt {
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+    field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CircularProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+  }
+
+  public final class CompositionLocalsKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+  }
+
+  public final class CornerRadiusKt {
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+    method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+  }
+
+  public abstract class GlanceAppWidget {
+    ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+    method public androidx.glance.appwidget.SizeMode getSizeMode();
+    method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+    method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public androidx.glance.appwidget.SizeMode sizeMode;
+    property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+  }
+
+  public final class GlanceAppWidgetKt {
+    method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+    method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+  }
+
+  public final class GlanceAppWidgetManager {
+    ctor public GlanceAppWidgetManager(android.content.Context context);
+    method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+    method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+    method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+    method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+    method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+  }
+
+  public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+    ctor public GlanceAppWidgetReceiver();
+    method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+    property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+    field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+    field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+  }
+
+  public static final class GlanceAppWidgetReceiver.Companion {
+  }
+
+  public final class ImageProvidersKt {
+    method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+  }
+
+  public final class LinearProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+    method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+    property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+    property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+    field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+    method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+    field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+    method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+  }
+
+  public sealed interface SizeMode {
+  }
+
+  public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+  }
+
+  public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+    ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+    method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+    property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+  }
+
+  public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+    field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+  }
+
+  public abstract sealed class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+    field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+  }
+
+}
+
+package androidx.glance.appwidget.action {
+
+  public interface ActionCallback {
+    method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class RunCallbackActionKt {
+    method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class SendBroadcastActionKt {
+    method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+    method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+    method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+    method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+  }
+
+  public final class StartActivityIntentActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+  }
+
+  public final class StartServiceActionKt {
+    method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+    method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+    method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+    method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+  }
+
+  public final class ToggleableKt {
+    method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+    property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+  }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+  public abstract sealed class GridCells {
+  }
+
+  @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Adaptive(float minSize);
+    method public float getMinSize();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+  }
+
+  public final class LazyListKt {
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyListScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  @kotlin.DslMarker public @interface LazyScopeMarker {
+  }
+
+  public final class LazyVerticalGridKt {
+    method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+    method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public static final class LazyVerticalGridScope.Companion {
+    field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+}
+
+package androidx.glance.appwidget.state {
+
+  public final class GlanceAppWidgetStateKt {
+    method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+    method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+}
+
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 3eaf94a..158c764 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -36,10 +36,7 @@
 AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project, /* isMultiplatformEnabled= */ false)
 
 dependencies {
-    bundleInside(project(
-            path: ":glance:glance-appwidget-proto",
-            configuration: "export"
-    ))
+    bundleInside(project(path: ":glance:glance-appwidget-proto", configuration: "export"))
 
     api(project(":glance:glance"))
     api("androidx.annotation:annotation:1.1.0")
@@ -73,6 +70,7 @@
 
     androidTestImplementation(project(":test:screenshot:screenshot"))
     androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
+    androidTestImplementation("androidx.room:room-runtime:2.4.3")
     androidTestImplementation('androidx.core:core-ktx:1.7.0')
     androidTestImplementation("androidx.work:work-testing:2.7.1")
     androidTestImplementation(libs.espressoCore)
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
index 2430d3f..01b903d 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
@@ -36,6 +36,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import androidx.work.WorkManager
+import androidx.work.impl.WorkManagerImpl
 import androidx.work.testing.WorkManagerTestInitHelper
 import com.google.common.truth.Truth.assertThat
 import java.lang.ref.WeakReference
@@ -111,6 +112,8 @@
                 mUiAutomation.dropShellPermissionIdentity()
             }
             WorkManager.getInstance(mContext).cancelAllWork()
+            // TODO(b/242026176): remove this once WorkManager allows closing the test database.
+            WorkManagerImpl.getInstance(context).workDatabase.close()
         }
     }
 
diff --git a/glance/glance-material/api/1.0.0-beta01.txt b/glance/glance-material/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material/api/res-1.0.0-beta01.txt b/glance/glance-material/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-material/api/restricted_1.0.0-beta01.txt b/glance/glance-material/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material3/api/1.0.0-beta01.txt b/glance/glance-material3/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-material3/api/res-1.0.0-beta01.txt b/glance/glance-material3/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material3/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-material3/api/restricted_1.0.0-beta01.txt b/glance/glance-material3/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-preview/api/1.0.0-beta01.txt b/glance/glance-preview/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-preview/api/1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..1fc4543
--- /dev/null
+++ b/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+  @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+  }
+
+  @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface GlancePreview {
+    method public abstract String surface();
+    property public abstract String surface;
+  }
+
+  @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface GlancePreview.Container {
+    method public abstract androidx.glance.preview.GlancePreview[] value();
+  }
+
+  @androidx.glance.preview.ExperimentalGlancePreviewApi public final class Surfaces {
+    field public static final String APP_WIDGET = "AppWidget";
+    field public static final androidx.glance.preview.Surfaces INSTANCE;
+    field public static final String TILE = "Tile";
+  }
+
+}
+
diff --git a/glance/glance-preview/api/res-1.0.0-beta01.txt b/glance/glance-preview/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-preview/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-preview/api/restricted_1.0.0-beta01.txt b/glance/glance-preview/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-preview/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-preview/build.gradle b/glance/glance-preview/build.gradle
index 61462cc..844dcee 100644
--- a/glance/glance-preview/build.gradle
+++ b/glance/glance-preview/build.gradle
@@ -26,6 +26,7 @@
 androidx {
     name = "Android Glance Preview"
     type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.GLANCE_PREVIEW
     inceptionYear = "2022"
     description = "Glance preview library. This library provides the API required for marking the" +
             "glance @Composable components that should have preview in the Android Studio."
diff --git a/glance/glance-template/build.gradle b/glance/glance-template/build.gradle
index 1fd8868..a74f078 100644
--- a/glance/glance-template/build.gradle
+++ b/glance/glance-template/build.gradle
@@ -42,7 +42,6 @@
     implementation("androidx.work:work-runtime:2.7.1")
     implementation("androidx.work:work-runtime-ktx:2.7.1")
     implementation(libs.kotlinStdlib)
-    implementation(project(":compose:runtime:runtime"))
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
     implementation("androidx.annotation:annotation-experimental:1.3.0")
@@ -76,6 +75,7 @@
 androidx {
     name = "Glance Templates Library"
     type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.GLANCE_TEMPLATE
     inceptionYear = "2021"
     description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
             "Compose-style API."
diff --git a/glance/glance-wear-tiles-preview/build.gradle b/glance/glance-wear-tiles-preview/build.gradle
index 1736a49..a5836b4 100644
--- a/glance/glance-wear-tiles-preview/build.gradle
+++ b/glance/glance-wear-tiles-preview/build.gradle
@@ -53,6 +53,7 @@
 androidx {
     name = "Android Glance Wear Tiles Preview"
     type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.GLANCE_WEAR_TILES
     inceptionYear = "2022"
     description = "Glance tooling library. This library provides the API required for the " +
             "GlanceTileService components and its Glance @Composable to be previewable in the IDE."
diff --git a/glance/glance-wear-tiles/build.gradle b/glance/glance-wear-tiles/build.gradle
index bcfb47e..155bfab 100644
--- a/glance/glance-wear-tiles/build.gradle
+++ b/glance/glance-wear-tiles/build.gradle
@@ -38,9 +38,8 @@
 
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesGuava)
-    implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
-    implementation(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
-    implementation(projectOrArtifact(":lifecycle:lifecycle-service"))
+    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
+    implementation("androidx.lifecycle:lifecycle-service:2.6.1")
 
     testImplementation(libs.testCore)
     testImplementation(libs.testRules)
@@ -92,6 +91,7 @@
 androidx {
     name = "Glance for Wear Tiles"
     type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.GLANCE_WEAR_TILES
     inceptionYear = "2021"
     description = "Glance allows developers to build layouts for Wear Tiles using a Jetpack " +
             "Compose-style API."
diff --git a/glance/glance/api/1.0.0-beta01.txt b/glance/glance/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+  }
+
+  public final class ButtonColors {
+    method public androidx.glance.unit.ColorProvider getBackgroundColor();
+    method public androidx.glance.unit.ColorProvider getContentColor();
+    property public final androidx.glance.unit.ColorProvider backgroundColor;
+    property public final androidx.glance.unit.ColorProvider contentColor;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+    field public static final androidx.glance.ButtonDefaults INSTANCE;
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+  }
+
+  public final class ColorFilter {
+    field public static final androidx.glance.ColorFilter.Companion Companion;
+  }
+
+  public static final class ColorFilter.Companion {
+    method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+  }
+
+  public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+    ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class CompositionLocalsKt {
+    method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+    method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+  }
+
+  @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+  }
+
+  public interface GlanceId {
+  }
+
+  @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+    method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+    field public static final androidx.glance.GlanceModifier.Companion Companion;
+  }
+
+  public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+    method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class GlanceTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+    field public static final androidx.glance.GlanceTheme INSTANCE;
+  }
+
+  public final class GlanceThemeKt {
+    method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ImageKt {
+    method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+    method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+    method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+  }
+
+  public interface ImageProvider {
+  }
+
+  public enum Visibility {
+    method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.glance.Visibility[] values();
+    enum_constant public static final androidx.glance.Visibility Gone;
+    enum_constant public static final androidx.glance.Visibility Invisible;
+    enum_constant public static final androidx.glance.Visibility Visible;
+  }
+
+  public final class VisibilityKt {
+    method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+  }
+
+}
+
+package androidx.glance.action {
+
+  public interface Action {
+  }
+
+  public final class ActionKt {
+    method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public abstract class ActionParameters {
+    method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public abstract boolean isEmpty();
+  }
+
+  public static final class ActionParameters.Key<T> {
+    ctor public ActionParameters.Key(String name);
+    method public String getName();
+    method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+    property public final String name;
+  }
+
+  public static final class ActionParameters.Pair<T> {
+  }
+
+  public final class ActionParametersKt {
+    method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+    method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+    method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+  }
+
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+    method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public void clear();
+    method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public boolean isEmpty();
+    method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+  }
+
+  public final class StartActivityActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+    method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+}
+
+package androidx.glance.color {
+
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+    property public final androidx.glance.unit.ColorProvider background;
+    property public final androidx.glance.unit.ColorProvider error;
+    property public final androidx.glance.unit.ColorProvider errorContainer;
+    property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+    property public final androidx.glance.unit.ColorProvider inversePrimary;
+    property public final androidx.glance.unit.ColorProvider inverseSurface;
+    property public final androidx.glance.unit.ColorProvider onBackground;
+    property public final androidx.glance.unit.ColorProvider onError;
+    property public final androidx.glance.unit.ColorProvider onErrorContainer;
+    property public final androidx.glance.unit.ColorProvider onPrimary;
+    property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSecondary;
+    property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSurface;
+    property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+    property public final androidx.glance.unit.ColorProvider onTertiary;
+    property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+    property public final androidx.glance.unit.ColorProvider outline;
+    property public final androidx.glance.unit.ColorProvider primary;
+    property public final androidx.glance.unit.ColorProvider primaryContainer;
+    property public final androidx.glance.unit.ColorProvider secondary;
+    property public final androidx.glance.unit.ColorProvider secondaryContainer;
+    property public final androidx.glance.unit.ColorProvider surface;
+    property public final androidx.glance.unit.ColorProvider surfaceVariant;
+    property public final androidx.glance.unit.ColorProvider tertiary;
+    property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+  }
+
+  public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+  }
+
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
+}
+
+package androidx.glance.layout {
+
+  public final class Alignment {
+    ctor public Alignment(int horizontal, int vertical);
+    method public int getHorizontal();
+    method public int getVertical();
+    property public final int horizontal;
+    property public final int vertical;
+    field public static final androidx.glance.layout.Alignment.Companion Companion;
+  }
+
+  public static final class Alignment.Companion {
+    method public int getBottom();
+    method public androidx.glance.layout.Alignment getBottomCenter();
+    method public androidx.glance.layout.Alignment getBottomEnd();
+    method public androidx.glance.layout.Alignment getBottomStart();
+    method public androidx.glance.layout.Alignment getCenter();
+    method public androidx.glance.layout.Alignment getCenterEnd();
+    method public int getCenterHorizontally();
+    method public androidx.glance.layout.Alignment getCenterStart();
+    method public int getCenterVertically();
+    method public int getEnd();
+    method public int getStart();
+    method public int getTop();
+    method public androidx.glance.layout.Alignment getTopCenter();
+    method public androidx.glance.layout.Alignment getTopEnd();
+    method public androidx.glance.layout.Alignment getTopStart();
+    property public final int Bottom;
+    property public final androidx.glance.layout.Alignment BottomCenter;
+    property public final androidx.glance.layout.Alignment BottomEnd;
+    property public final androidx.glance.layout.Alignment BottomStart;
+    property public final androidx.glance.layout.Alignment Center;
+    property public final androidx.glance.layout.Alignment CenterEnd;
+    property public final int CenterHorizontally;
+    property public final androidx.glance.layout.Alignment CenterStart;
+    property public final int CenterVertically;
+    property public final int End;
+    property public final int Start;
+    property public final int Top;
+    property public final androidx.glance.layout.Alignment TopCenter;
+    property public final androidx.glance.layout.Alignment TopEnd;
+    property public final androidx.glance.layout.Alignment TopStart;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+    field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+  }
+
+  public static final class Alignment.Horizontal.Companion {
+    method public int getCenterHorizontally();
+    method public int getEnd();
+    method public int getStart();
+    property public final int CenterHorizontally;
+    property public final int End;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+    field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+  }
+
+  public static final class Alignment.Vertical.Companion {
+    method public int getBottom();
+    method public int getCenterVertically();
+    method public int getTop();
+    property public final int Bottom;
+    property public final int CenterVertically;
+    property public final int Top;
+  }
+
+  public final class BoxKt {
+    method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ColumnKt {
+    method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  public interface ColumnScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  @kotlin.jvm.JvmInline public final value class ContentScale {
+    ctor public ContentScale(int value);
+    field public static final androidx.glance.layout.ContentScale.Companion Companion;
+  }
+
+  public static final class ContentScale.Companion {
+    method public int getCrop();
+    method public int getFillBounds();
+    method public int getFit();
+    property public final int Crop;
+    property public final int FillBounds;
+    property public final int Fit;
+  }
+
+  public final class PaddingKt {
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+  }
+
+  public final class RowKt {
+    method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+  }
+
+  public interface RowScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  public final class SizeModifiersKt {
+    method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+    method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+  }
+
+  public final class SpacerKt {
+    method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+  }
+
+}
+
+package androidx.glance.semantics {
+
+  public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+    ctor public SemanticsConfiguration();
+    method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+  public final class SemanticsModifierKt {
+    method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+  }
+
+  public final class SemanticsProperties {
+    method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+    property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+    field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+  }
+
+  public final class SemanticsPropertiesKt {
+    method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+    method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+  }
+
+  public final class SemanticsPropertyKey<T> {
+    ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+    method public String getName();
+    method public T? merge(T? parentValue, T childValue);
+    property public final String name;
+  }
+
+  public interface SemanticsPropertyReceiver {
+    method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+}
+
+package androidx.glance.session {
+
+  public final class SessionManagerKt {
+  }
+
+}
+
+package androidx.glance.state {
+
+  public interface GlanceStateDefinition<T> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+  }
+
+  public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+    field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+  }
+
+}
+
+package androidx.glance.text {
+
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontStyle {
+    field public static final androidx.glance.text.FontStyle.Companion Companion;
+  }
+
+  public static final class FontStyle.Companion {
+    method public int getItalic();
+    method public int getNormal();
+    method public java.util.List<androidx.glance.text.FontStyle> values();
+    property public final int Italic;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontWeight {
+    method public int getValue();
+    property public final int value;
+    field public static final androidx.glance.text.FontWeight.Companion Companion;
+  }
+
+  public static final class FontWeight.Companion {
+    method public int getBold();
+    method public int getMedium();
+    method public int getNormal();
+    property public final int Bold;
+    property public final int Medium;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextAlign {
+    field public static final androidx.glance.text.TextAlign.Companion Companion;
+  }
+
+  public static final class TextAlign.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    method public int getLeft();
+    method public int getRight();
+    method public int getStart();
+    method public java.util.List<androidx.glance.text.TextAlign> values();
+    property public final int Center;
+    property public final int End;
+    property public final int Left;
+    property public final int Right;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextDecoration {
+    method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+    method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+    field public static final androidx.glance.text.TextDecoration.Companion Companion;
+  }
+
+  public static final class TextDecoration.Companion {
+    method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+    method public int getLineThrough();
+    method public int getNone();
+    method public int getUnderline();
+    property public final int LineThrough;
+    property public final int None;
+    property public final int Underline;
+  }
+
+  public final class TextDefaults {
+    method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+    method public androidx.glance.text.TextStyle getDefaultTextStyle();
+    property public final androidx.glance.unit.ColorProvider defaultTextColor;
+    property public final androidx.glance.text.TextStyle defaultTextStyle;
+    field public static final androidx.glance.text.TextDefaults INSTANCE;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextStyle {
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
+    method public androidx.compose.ui.unit.TextUnit? getFontSize();
+    method public androidx.glance.text.FontStyle? getFontStyle();
+    method public androidx.glance.text.FontWeight? getFontWeight();
+    method public androidx.glance.text.TextAlign? getTextAlign();
+    method public androidx.glance.text.TextDecoration? getTextDecoration();
+    property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
+    property public final androidx.compose.ui.unit.TextUnit? fontSize;
+    property public final androidx.glance.text.FontStyle? fontStyle;
+    property public final androidx.glance.text.FontWeight? fontWeight;
+    property public final androidx.glance.text.TextAlign? textAlign;
+    property public final androidx.glance.text.TextDecoration? textDecoration;
+  }
+
+}
+
+package androidx.glance.unit {
+
+  public interface ColorProvider {
+    method public long getColor(android.content.Context context);
+  }
+
+  public final class ColorProviderKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+  }
+
+}
+
diff --git a/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+  }
+
+  public final class ButtonColors {
+    method public androidx.glance.unit.ColorProvider getBackgroundColor();
+    method public androidx.glance.unit.ColorProvider getContentColor();
+    property public final androidx.glance.unit.ColorProvider backgroundColor;
+    property public final androidx.glance.unit.ColorProvider contentColor;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+    field public static final androidx.glance.ButtonDefaults INSTANCE;
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+  }
+
+  public final class ColorFilter {
+    field public static final androidx.glance.ColorFilter.Companion Companion;
+  }
+
+  public static final class ColorFilter.Companion {
+    method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+  }
+
+  public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+    ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class CompositionLocalsKt {
+    method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+    method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+  }
+
+  @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+  }
+
+  public interface GlanceId {
+  }
+
+  @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+    method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+    field public static final androidx.glance.GlanceModifier.Companion Companion;
+  }
+
+  public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+    method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class GlanceTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+    field public static final androidx.glance.GlanceTheme INSTANCE;
+  }
+
+  public final class GlanceThemeKt {
+    method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ImageKt {
+    method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+    method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+    method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+  }
+
+  public interface ImageProvider {
+  }
+
+  public enum Visibility {
+    method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.glance.Visibility[] values();
+    enum_constant public static final androidx.glance.Visibility Gone;
+    enum_constant public static final androidx.glance.Visibility Invisible;
+    enum_constant public static final androidx.glance.Visibility Visible;
+  }
+
+  public final class VisibilityKt {
+    method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+  }
+
+}
+
+package androidx.glance.action {
+
+  public interface Action {
+  }
+
+  public final class ActionKt {
+    method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public abstract class ActionParameters {
+    method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public abstract boolean isEmpty();
+  }
+
+  public static final class ActionParameters.Key<T> {
+    ctor public ActionParameters.Key(String name);
+    method public String getName();
+    method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+    property public final String name;
+  }
+
+  public static final class ActionParameters.Pair<T> {
+  }
+
+  public final class ActionParametersKt {
+    method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+    method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+    method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+  }
+
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+    method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public void clear();
+    method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public boolean isEmpty();
+    method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+  }
+
+  public final class StartActivityActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+    method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+}
+
+package androidx.glance.color {
+
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+    property public final androidx.glance.unit.ColorProvider background;
+    property public final androidx.glance.unit.ColorProvider error;
+    property public final androidx.glance.unit.ColorProvider errorContainer;
+    property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+    property public final androidx.glance.unit.ColorProvider inversePrimary;
+    property public final androidx.glance.unit.ColorProvider inverseSurface;
+    property public final androidx.glance.unit.ColorProvider onBackground;
+    property public final androidx.glance.unit.ColorProvider onError;
+    property public final androidx.glance.unit.ColorProvider onErrorContainer;
+    property public final androidx.glance.unit.ColorProvider onPrimary;
+    property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSecondary;
+    property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSurface;
+    property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+    property public final androidx.glance.unit.ColorProvider onTertiary;
+    property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+    property public final androidx.glance.unit.ColorProvider outline;
+    property public final androidx.glance.unit.ColorProvider primary;
+    property public final androidx.glance.unit.ColorProvider primaryContainer;
+    property public final androidx.glance.unit.ColorProvider secondary;
+    property public final androidx.glance.unit.ColorProvider secondaryContainer;
+    property public final androidx.glance.unit.ColorProvider surface;
+    property public final androidx.glance.unit.ColorProvider surfaceVariant;
+    property public final androidx.glance.unit.ColorProvider tertiary;
+    property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+  }
+
+  public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+  }
+
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
+}
+
+package androidx.glance.layout {
+
+  public final class Alignment {
+    ctor public Alignment(int horizontal, int vertical);
+    method public int getHorizontal();
+    method public int getVertical();
+    property public final int horizontal;
+    property public final int vertical;
+    field public static final androidx.glance.layout.Alignment.Companion Companion;
+  }
+
+  public static final class Alignment.Companion {
+    method public int getBottom();
+    method public androidx.glance.layout.Alignment getBottomCenter();
+    method public androidx.glance.layout.Alignment getBottomEnd();
+    method public androidx.glance.layout.Alignment getBottomStart();
+    method public androidx.glance.layout.Alignment getCenter();
+    method public androidx.glance.layout.Alignment getCenterEnd();
+    method public int getCenterHorizontally();
+    method public androidx.glance.layout.Alignment getCenterStart();
+    method public int getCenterVertically();
+    method public int getEnd();
+    method public int getStart();
+    method public int getTop();
+    method public androidx.glance.layout.Alignment getTopCenter();
+    method public androidx.glance.layout.Alignment getTopEnd();
+    method public androidx.glance.layout.Alignment getTopStart();
+    property public final int Bottom;
+    property public final androidx.glance.layout.Alignment BottomCenter;
+    property public final androidx.glance.layout.Alignment BottomEnd;
+    property public final androidx.glance.layout.Alignment BottomStart;
+    property public final androidx.glance.layout.Alignment Center;
+    property public final androidx.glance.layout.Alignment CenterEnd;
+    property public final int CenterHorizontally;
+    property public final androidx.glance.layout.Alignment CenterStart;
+    property public final int CenterVertically;
+    property public final int End;
+    property public final int Start;
+    property public final int Top;
+    property public final androidx.glance.layout.Alignment TopCenter;
+    property public final androidx.glance.layout.Alignment TopEnd;
+    property public final androidx.glance.layout.Alignment TopStart;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+    field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+  }
+
+  public static final class Alignment.Horizontal.Companion {
+    method public int getCenterHorizontally();
+    method public int getEnd();
+    method public int getStart();
+    property public final int CenterHorizontally;
+    property public final int End;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+    field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+  }
+
+  public static final class Alignment.Vertical.Companion {
+    method public int getBottom();
+    method public int getCenterVertically();
+    method public int getTop();
+    property public final int Bottom;
+    property public final int CenterVertically;
+    property public final int Top;
+  }
+
+  public final class BoxKt {
+    method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ColumnKt {
+    method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  public interface ColumnScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  @kotlin.jvm.JvmInline public final value class ContentScale {
+    ctor public ContentScale(int value);
+    field public static final androidx.glance.layout.ContentScale.Companion Companion;
+  }
+
+  public static final class ContentScale.Companion {
+    method public int getCrop();
+    method public int getFillBounds();
+    method public int getFit();
+    property public final int Crop;
+    property public final int FillBounds;
+    property public final int Fit;
+  }
+
+  public final class PaddingKt {
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+  }
+
+  public final class RowKt {
+    method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+  }
+
+  public interface RowScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  public final class SizeModifiersKt {
+    method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+    method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+  }
+
+  public final class SpacerKt {
+    method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+  }
+
+}
+
+package androidx.glance.semantics {
+
+  public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+    ctor public SemanticsConfiguration();
+    method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+  public final class SemanticsModifierKt {
+    method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+  }
+
+  public final class SemanticsProperties {
+    method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+    property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+    field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+  }
+
+  public final class SemanticsPropertiesKt {
+    method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+    method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+  }
+
+  public final class SemanticsPropertyKey<T> {
+    ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+    method public String getName();
+    method public T? merge(T? parentValue, T childValue);
+    property public final String name;
+  }
+
+  public interface SemanticsPropertyReceiver {
+    method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+}
+
+package androidx.glance.session {
+
+  public final class SessionManagerKt {
+  }
+
+}
+
+package androidx.glance.state {
+
+  public interface GlanceStateDefinition<T> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+  }
+
+  public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+    field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+  }
+
+}
+
+package androidx.glance.text {
+
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontStyle {
+    field public static final androidx.glance.text.FontStyle.Companion Companion;
+  }
+
+  public static final class FontStyle.Companion {
+    method public int getItalic();
+    method public int getNormal();
+    method public java.util.List<androidx.glance.text.FontStyle> values();
+    property public final int Italic;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontWeight {
+    method public int getValue();
+    property public final int value;
+    field public static final androidx.glance.text.FontWeight.Companion Companion;
+  }
+
+  public static final class FontWeight.Companion {
+    method public int getBold();
+    method public int getMedium();
+    method public int getNormal();
+    property public final int Bold;
+    property public final int Medium;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextAlign {
+    field public static final androidx.glance.text.TextAlign.Companion Companion;
+  }
+
+  public static final class TextAlign.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    method public int getLeft();
+    method public int getRight();
+    method public int getStart();
+    method public java.util.List<androidx.glance.text.TextAlign> values();
+    property public final int Center;
+    property public final int End;
+    property public final int Left;
+    property public final int Right;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextDecoration {
+    method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+    method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+    field public static final androidx.glance.text.TextDecoration.Companion Companion;
+  }
+
+  public static final class TextDecoration.Companion {
+    method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+    method public int getLineThrough();
+    method public int getNone();
+    method public int getUnderline();
+    property public final int LineThrough;
+    property public final int None;
+    property public final int Underline;
+  }
+
+  public final class TextDefaults {
+    method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+    method public androidx.glance.text.TextStyle getDefaultTextStyle();
+    property public final androidx.glance.unit.ColorProvider defaultTextColor;
+    property public final androidx.glance.text.TextStyle defaultTextStyle;
+    field public static final androidx.glance.text.TextDefaults INSTANCE;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextStyle {
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
+    method public androidx.compose.ui.unit.TextUnit? getFontSize();
+    method public androidx.glance.text.FontStyle? getFontStyle();
+    method public androidx.glance.text.FontWeight? getFontWeight();
+    method public androidx.glance.text.TextAlign? getTextAlign();
+    method public androidx.glance.text.TextDecoration? getTextDecoration();
+    property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
+    property public final androidx.compose.ui.unit.TextUnit? fontSize;
+    property public final androidx.glance.text.FontStyle? fontStyle;
+    property public final androidx.glance.text.FontWeight? fontWeight;
+    property public final androidx.glance.text.TextAlign? textAlign;
+    property public final androidx.glance.text.TextDecoration? textDecoration;
+  }
+
+}
+
+package androidx.glance.unit {
+
+  public interface ColorProvider {
+    method public long getColor(android.content.Context context);
+  }
+
+  public final class ColorProviderKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+  }
+
+}
+
diff --git a/glance/glance/api/res-1.0.0-beta01.txt b/glance/glance/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance/api/res-1.0.0-beta01.txt
diff --git a/glance/glance/api/restricted_1.0.0-beta01.txt b/glance/glance/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+  public final class BackgroundKt {
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+    method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+  }
+
+  public final class ButtonColors {
+    method public androidx.glance.unit.ColorProvider getBackgroundColor();
+    method public androidx.glance.unit.ColorProvider getContentColor();
+    property public final androidx.glance.unit.ColorProvider backgroundColor;
+    property public final androidx.glance.unit.ColorProvider contentColor;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+    field public static final androidx.glance.ButtonDefaults INSTANCE;
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+  }
+
+  public final class ColorFilter {
+    field public static final androidx.glance.ColorFilter.Companion Companion;
+  }
+
+  public static final class ColorFilter.Companion {
+    method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+  }
+
+  public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+    ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class CompositionLocalsKt {
+    method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+    method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+  }
+
+  @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+  }
+
+  public interface GlanceId {
+  }
+
+  @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+    method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+    field public static final androidx.glance.GlanceModifier.Companion Companion;
+  }
+
+  public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+    method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+    method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+    method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+    method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+  }
+
+  public final class GlanceTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+    field public static final androidx.glance.GlanceTheme INSTANCE;
+  }
+
+  public final class GlanceThemeKt {
+    method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ImageKt {
+    method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+    method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+    method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+  }
+
+  public interface ImageProvider {
+  }
+
+  public enum Visibility {
+    method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.glance.Visibility[] values();
+    enum_constant public static final androidx.glance.Visibility Gone;
+    enum_constant public static final androidx.glance.Visibility Invisible;
+    enum_constant public static final androidx.glance.Visibility Visible;
+  }
+
+  public final class VisibilityKt {
+    method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+  }
+
+}
+
+package androidx.glance.action {
+
+  public interface Action {
+  }
+
+  public final class ActionKt {
+    method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public abstract class ActionParameters {
+    method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public abstract boolean isEmpty();
+  }
+
+  public static final class ActionParameters.Key<T> {
+    ctor public ActionParameters.Key(String name);
+    method public String getName();
+    method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+    property public final String name;
+  }
+
+  public static final class ActionParameters.Pair<T> {
+  }
+
+  public final class ActionParametersKt {
+    method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+    method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+    method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+    method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+  }
+
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+  public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+    method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+    method public void clear();
+    method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+    method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+    method public boolean isEmpty();
+    method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+    method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+  }
+
+  public final class StartActivityActionKt {
+    method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+    method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+    method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+  }
+
+}
+
+package androidx.glance.color {
+
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+    property public final androidx.glance.unit.ColorProvider background;
+    property public final androidx.glance.unit.ColorProvider error;
+    property public final androidx.glance.unit.ColorProvider errorContainer;
+    property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+    property public final androidx.glance.unit.ColorProvider inversePrimary;
+    property public final androidx.glance.unit.ColorProvider inverseSurface;
+    property public final androidx.glance.unit.ColorProvider onBackground;
+    property public final androidx.glance.unit.ColorProvider onError;
+    property public final androidx.glance.unit.ColorProvider onErrorContainer;
+    property public final androidx.glance.unit.ColorProvider onPrimary;
+    property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSecondary;
+    property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+    property public final androidx.glance.unit.ColorProvider onSurface;
+    property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+    property public final androidx.glance.unit.ColorProvider onTertiary;
+    property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+    property public final androidx.glance.unit.ColorProvider outline;
+    property public final androidx.glance.unit.ColorProvider primary;
+    property public final androidx.glance.unit.ColorProvider primaryContainer;
+    property public final androidx.glance.unit.ColorProvider secondary;
+    property public final androidx.glance.unit.ColorProvider secondaryContainer;
+    property public final androidx.glance.unit.ColorProvider surface;
+    property public final androidx.glance.unit.ColorProvider surfaceVariant;
+    property public final androidx.glance.unit.ColorProvider tertiary;
+    property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+  }
+
+  public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+  }
+
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
+}
+
+package androidx.glance.layout {
+
+  public final class Alignment {
+    ctor public Alignment(int horizontal, int vertical);
+    method public int getHorizontal();
+    method public int getVertical();
+    property public final int horizontal;
+    property public final int vertical;
+    field public static final androidx.glance.layout.Alignment.Companion Companion;
+  }
+
+  public static final class Alignment.Companion {
+    method public int getBottom();
+    method public androidx.glance.layout.Alignment getBottomCenter();
+    method public androidx.glance.layout.Alignment getBottomEnd();
+    method public androidx.glance.layout.Alignment getBottomStart();
+    method public androidx.glance.layout.Alignment getCenter();
+    method public androidx.glance.layout.Alignment getCenterEnd();
+    method public int getCenterHorizontally();
+    method public androidx.glance.layout.Alignment getCenterStart();
+    method public int getCenterVertically();
+    method public int getEnd();
+    method public int getStart();
+    method public int getTop();
+    method public androidx.glance.layout.Alignment getTopCenter();
+    method public androidx.glance.layout.Alignment getTopEnd();
+    method public androidx.glance.layout.Alignment getTopStart();
+    property public final int Bottom;
+    property public final androidx.glance.layout.Alignment BottomCenter;
+    property public final androidx.glance.layout.Alignment BottomEnd;
+    property public final androidx.glance.layout.Alignment BottomStart;
+    property public final androidx.glance.layout.Alignment Center;
+    property public final androidx.glance.layout.Alignment CenterEnd;
+    property public final int CenterHorizontally;
+    property public final androidx.glance.layout.Alignment CenterStart;
+    property public final int CenterVertically;
+    property public final int End;
+    property public final int Start;
+    property public final int Top;
+    property public final androidx.glance.layout.Alignment TopCenter;
+    property public final androidx.glance.layout.Alignment TopEnd;
+    property public final androidx.glance.layout.Alignment TopStart;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+    field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+  }
+
+  public static final class Alignment.Horizontal.Companion {
+    method public int getCenterHorizontally();
+    method public int getEnd();
+    method public int getStart();
+    property public final int CenterHorizontally;
+    property public final int End;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+    field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+  }
+
+  public static final class Alignment.Vertical.Companion {
+    method public int getBottom();
+    method public int getCenterVertically();
+    method public int getTop();
+    property public final int Bottom;
+    property public final int CenterVertically;
+    property public final int Top;
+  }
+
+  public final class BoxKt {
+    method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ColumnKt {
+    method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  public interface ColumnScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  @kotlin.jvm.JvmInline public final value class ContentScale {
+    ctor public ContentScale(int value);
+    field public static final androidx.glance.layout.ContentScale.Companion Companion;
+  }
+
+  public static final class ContentScale.Companion {
+    method public int getCrop();
+    method public int getFillBounds();
+    method public int getFit();
+    property public final int Crop;
+    property public final int FillBounds;
+    property public final int Fit;
+  }
+
+  public final class PaddingKt {
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+    method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+  }
+
+  public final class RowKt {
+    method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+  }
+
+  public interface RowScope {
+    method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+  }
+
+  public final class SizeModifiersKt {
+    method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+    method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+    method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+    method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+    method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+    method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+  }
+
+  public final class SpacerKt {
+    method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+  }
+
+}
+
+package androidx.glance.semantics {
+
+  public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+    ctor public SemanticsConfiguration();
+    method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+    method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+  public final class SemanticsModifierKt {
+    method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+  }
+
+  public final class SemanticsProperties {
+    method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+    property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+    field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+  }
+
+  public final class SemanticsPropertiesKt {
+    method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+    method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+  }
+
+  public final class SemanticsPropertyKey<T> {
+    ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+    method public String getName();
+    method public T? merge(T? parentValue, T childValue);
+    property public final String name;
+  }
+
+  public interface SemanticsPropertyReceiver {
+    method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+  }
+
+}
+
+package androidx.glance.session {
+
+  public final class SessionManagerKt {
+  }
+
+}
+
+package androidx.glance.state {
+
+  public interface GlanceStateDefinition<T> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+  }
+
+  public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+    method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+    method public java.io.File getLocation(android.content.Context context, String fileKey);
+    field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+  }
+
+}
+
+package androidx.glance.text {
+
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontStyle {
+    field public static final androidx.glance.text.FontStyle.Companion Companion;
+  }
+
+  public static final class FontStyle.Companion {
+    method public int getItalic();
+    method public int getNormal();
+    method public java.util.List<androidx.glance.text.FontStyle> values();
+    property public final int Italic;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class FontWeight {
+    method public int getValue();
+    property public final int value;
+    field public static final androidx.glance.text.FontWeight.Companion Companion;
+  }
+
+  public static final class FontWeight.Companion {
+    method public int getBold();
+    method public int getMedium();
+    method public int getNormal();
+    property public final int Bold;
+    property public final int Medium;
+    property public final int Normal;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextAlign {
+    field public static final androidx.glance.text.TextAlign.Companion Companion;
+  }
+
+  public static final class TextAlign.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    method public int getLeft();
+    method public int getRight();
+    method public int getStart();
+    method public java.util.List<androidx.glance.text.TextAlign> values();
+    property public final int Center;
+    property public final int End;
+    property public final int Left;
+    property public final int Right;
+    property public final int Start;
+  }
+
+  @kotlin.jvm.JvmInline public final value class TextDecoration {
+    method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+    method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+    field public static final androidx.glance.text.TextDecoration.Companion Companion;
+  }
+
+  public static final class TextDecoration.Companion {
+    method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+    method public int getLineThrough();
+    method public int getNone();
+    method public int getUnderline();
+    property public final int LineThrough;
+    property public final int None;
+    property public final int Underline;
+  }
+
+  public final class TextDefaults {
+    method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+    method public androidx.glance.text.TextStyle getDefaultTextStyle();
+    property public final androidx.glance.unit.ColorProvider defaultTextColor;
+    property public final androidx.glance.text.TextStyle defaultTextStyle;
+    field public static final androidx.glance.text.TextDefaults INSTANCE;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextStyle {
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
+    method public androidx.compose.ui.unit.TextUnit? getFontSize();
+    method public androidx.glance.text.FontStyle? getFontStyle();
+    method public androidx.glance.text.FontWeight? getFontWeight();
+    method public androidx.glance.text.TextAlign? getTextAlign();
+    method public androidx.glance.text.TextDecoration? getTextDecoration();
+    property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
+    property public final androidx.compose.ui.unit.TextUnit? fontSize;
+    property public final androidx.glance.text.FontStyle? fontStyle;
+    property public final androidx.glance.text.FontWeight? fontWeight;
+    property public final androidx.glance.text.TextAlign? textAlign;
+    property public final androidx.glance.text.TextDecoration? textDecoration;
+  }
+
+}
+
+package androidx.glance.unit {
+
+  public interface ColorProvider {
+    method public long getColor(android.content.Context context);
+  }
+
+  public final class ColorProviderKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+  }
+
+}
+
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index b8fb797..68ec157 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -30,7 +30,7 @@
 dependencies {
 
     api("androidx.annotation:annotation:1.2.0")
-    api("androidx.compose.runtime:runtime:1.1.1")
+    api("androidx.compose.runtime:runtime:1.2.1")
     api("androidx.compose.ui:ui-graphics:1.1.1")
     api("androidx.compose.ui:ui-unit:1.1.1")
     api("androidx.datastore:datastore-core:1.0.0")
@@ -41,7 +41,6 @@
     implementation("androidx.work:work-runtime:2.7.1")
     implementation("androidx.work:work-runtime-ktx:2.7.1")
     implementation(libs.kotlinStdlib)
-    implementation(project(":compose:runtime:runtime"))
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
     implementation("androidx.annotation:annotation-experimental:1.3.0")
@@ -66,6 +65,7 @@
 
     androidTestImplementation('androidx.test:monitor:1.5.0')
     androidTestImplementation('androidx.core:core-ktx:1.7.0')
+    androidTestImplementation("androidx.room:room-runtime:2.4.3")
     androidTestImplementation("androidx.work:work-testing:2.7.1")
     androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
     androidTestImplementation(libs.kotlinCoroutinesTest)
diff --git a/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt b/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
index 9db9374..78fc16b 100644
--- a/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
+++ b/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
@@ -33,6 +33,7 @@
 import androidx.work.WorkManager
 import androidx.work.WorkerFactory
 import androidx.work.WorkerParameters
+import androidx.work.impl.WorkManagerImpl
 import androidx.work.testing.SynchronousExecutor
 import androidx.work.testing.WorkManagerTestInitHelper
 import com.google.common.truth.Truth.assertThat
@@ -115,6 +116,8 @@
         testScope.cancel()
         workerStateScope.cancel()
         WorkManager.getInstance(context).cancelAllWork()
+        // TODO(b/242026176): remove this once WorkManager allows closing the test database.
+        WorkManagerImpl.getInstance(context).workDatabase.close()
     }
 
     @Test
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 0d1f5e0..3431ba0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,13 +2,13 @@
 # -----------------------------------------------------------------------------
 # All of the following should be updated in sync.
 # -----------------------------------------------------------------------------
-androidGradlePlugin = "8.1.0-alpha11"
+androidGradlePlugin = "8.1.0-beta01"
 # NOTE: When updating the lint version we also need to update the `api` version
 # supported by `IssueRegistry`'s.' For e.g. r.android.com/1331903
-androidLint = "31.1.0-alpha11"
+androidLint = "31.1.0-beta01"
 # Once you have a chosen version of AGP to upgrade to, go to
 # https://developer.android.com/studio/archive and find the matching version of Studio.
-androidStudio = "2022.3.1.11"
+androidStudio = "2022.3.1.12"
 # -----------------------------------------------------------------------------
 
 androidGradlePluginMin = "7.0.4"
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
index 36adf59..50ccb37 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
@@ -37,6 +37,7 @@
 import java.util.concurrent.TimeUnit
 import kotlin.math.roundToInt
 import org.junit.Assert
+import org.junit.Assert.fail
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -623,6 +624,57 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testReleaseRemovedSurfaceCallbacks() {
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Any> {
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Any
+            ) {
+                // no-op
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Any>
+            ) {
+                // no-op
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Any>? = null
+        var surfaceView: FrontBufferedRendererTestActivity.TestSurfaceView? = null
+        val createLatch = CountDownLatch(1)
+        ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+            .moveToState(Lifecycle.State.CREATED)
+            .onActivity {
+                surfaceView = it.getSurfaceView()
+                renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                createLatch.countDown()
+            }
+
+        Assert.assertTrue(createLatch.await(3000, TimeUnit.MILLISECONDS))
+        // Capture surfaceView with local val to avoid Kotlin warnings regarding the surfaceView
+        // parameter changing potentially
+        val resolvedSurfaceView = surfaceView
+        try {
+            if (resolvedSurfaceView != null) {
+                Assert.assertEquals(1, resolvedSurfaceView.getCallbackCount())
+                renderer?.release(true)
+                renderer = null
+                Assert.assertEquals(0, resolvedSurfaceView.getCallbackCount())
+            } else {
+                fail("Unable to resolve SurfaceView, was the Activity created?")
+            }
+        } finally {
+            renderer?.release(true)
+        }
+    }
+
     @RequiresApi(Build.VERSION_CODES.Q)
     private fun CanvasFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
         if (this != null) {
@@ -640,7 +692,13 @@
         // See "b/277225133" these tests pass on cuttlefish + other devices but fail for some reason
         // FTL configured API level 33 emulator instances
         // Additionally some cuttlefish instances don't support rotation based testing (b/277764242)
-        !(Build.MODEL.contains("gphone") && Build.VERSION.SDK_INT == 33) &&
-            !(Build.MODEL.contains("Cuttlefish") &&
-                (Build.VERSION.SDK_INT == 30 || BuildCompat.isAtLeastV()))
+        isSupportedGphone() && isSupportedCuttlefish()
+
+    private fun isSupportedGphone() =
+        !(Build.MODEL.contains("gphone") &&
+            (Build.VERSION.SDK_INT == 33 || Build.VERSION.SDK_INT == 30))
+
+    private fun isSupportedCuttlefish() =
+        !(Build.MODEL.contains("Cuttlefish") &&
+            (Build.VERSION.SDK_INT == 30 || BuildCompat.isAtLeastV()))
 }
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
index addcfff..9fae68f 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
@@ -17,24 +17,60 @@
 package androidx.graphics.lowlatency
 
 import android.app.Activity
+import android.content.Context
 import android.os.Bundle
+import android.view.SurfaceHolder
 import android.view.SurfaceView
 import android.view.ViewGroup
 
 class FrontBufferedRendererTestActivity : Activity() {
 
-    private lateinit var mSurfaceView: SurfaceView
+    private lateinit var mSurfaceView: TestSurfaceView
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        val surfaceView = SurfaceView(this).also { mSurfaceView = it }
+        val surfaceView = TestSurfaceView(this).also { mSurfaceView = it }
         setContentView(surfaceView, ViewGroup.LayoutParams(WIDTH, HEIGHT))
     }
 
-    fun getSurfaceView(): SurfaceView = mSurfaceView
+    fun getSurfaceView(): TestSurfaceView = mSurfaceView
 
     companion object {
         const val WIDTH = 100
         const val HEIGHT = 100
     }
+
+    class TestSurfaceView(context: Context) : SurfaceView(context) {
+
+        private var mHolderWrapper: HolderWrapper? = null
+
+        override fun getHolder(): SurfaceHolder {
+            var wrapper = mHolderWrapper
+            if (wrapper == null) {
+                wrapper = HolderWrapper(super.getHolder()).also { mHolderWrapper = it }
+            }
+            return wrapper
+        }
+
+        fun getCallbackCount(): Int = mHolderWrapper?.mCallbacks?.size ?: 0
+
+        class HolderWrapper(val wrapped: SurfaceHolder) : SurfaceHolder by wrapped {
+
+            val mCallbacks = ArrayList<SurfaceHolder.Callback>()
+
+            override fun addCallback(callback: SurfaceHolder.Callback) {
+                if (!mCallbacks.contains(callback)) {
+                    mCallbacks.add(callback)
+                    wrapped.addCallback(callback)
+                }
+            }
+
+            override fun removeCallback(callback: SurfaceHolder.Callback) {
+                if (mCallbacks.contains(callback)) {
+                    mCallbacks.remove(callback)
+                    wrapped.removeCallback(callback)
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
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 15fbf2e..a849854 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
@@ -25,6 +25,7 @@
 import android.view.SurfaceHolder
 import android.view.SurfaceView
 import androidx.annotation.RequiresApi
+import androidx.graphics.opengl.GLRenderer
 import androidx.graphics.opengl.egl.EGLManager
 import androidx.graphics.surface.SurfaceControlCompat
 import androidx.graphics.surface.SurfaceControlUtils
@@ -37,9 +38,11 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
+import kotlin.IllegalStateException
 import kotlin.math.roundToInt
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Ignore
@@ -1218,6 +1221,92 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testReleaseRemovedSurfaceCallbacks() {
+        val callbacks = object : GLFrontBufferedRenderer.Callback<Any> {
+            override fun onDrawFrontBufferedLayer(
+                eglManager: EGLManager,
+                bufferInfo: BufferInfo,
+                transform: FloatArray,
+                param: Any
+            ) {
+                // NO-OP
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                eglManager: EGLManager,
+                bufferInfo: BufferInfo,
+                transform: FloatArray,
+                params: Collection<Any>
+            ) {
+                // NO-OP
+            }
+        }
+        var renderer: GLFrontBufferedRenderer<Any>? = null
+        var surfaceView: FrontBufferedRendererTestActivity.TestSurfaceView? = null
+        val createLatch = CountDownLatch(1)
+        ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+            .moveToState(Lifecycle.State.CREATED)
+            .onActivity {
+                surfaceView = it.getSurfaceView()
+                renderer = GLFrontBufferedRenderer(surfaceView!!, callbacks)
+                createLatch.countDown()
+            }
+
+        assertTrue(createLatch.await(3000, TimeUnit.MILLISECONDS))
+        // Capture surfaceView with local val to avoid Kotlin warnings regarding the surfaceView
+        // parameter changing potentially
+        val resolvedSurfaceView = surfaceView
+        try {
+            if (resolvedSurfaceView != null) {
+                assertEquals(1, resolvedSurfaceView.getCallbackCount())
+                val releaseLatch = CountDownLatch(1)
+                renderer!!.release(true) {
+                    releaseLatch.countDown()
+                }
+                assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
+                assertEquals(0, resolvedSurfaceView.getCallbackCount())
+                renderer = null
+            } else {
+                fail("Unable to resolve SurfaceView, was the test Activity created?")
+            }
+        } finally {
+            renderer?.blockingRelease()
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testGLFrontBufferedRendererCreationFromUnstartedGLRenderer() {
+        val callbacks = object : GLFrontBufferedRenderer.Callback<Any> {
+            override fun onDrawFrontBufferedLayer(
+                eglManager: EGLManager,
+                bufferInfo: BufferInfo,
+                transform: FloatArray,
+                param: Any
+            ) {
+                // NO-OP
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                eglManager: EGLManager,
+                bufferInfo: BufferInfo,
+                transform: FloatArray,
+                params: Collection<Any>
+            ) {
+                // NO-OP
+            }
+        }
+        ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+            .moveToState(Lifecycle.State.CREATED)
+            .onActivity {
+                assertThrows(IllegalStateException::class.java) {
+                    GLFrontBufferedRenderer(it.getSurfaceView(), callbacks, GLRenderer())
+                }
+            }
+    }
+
     @RequiresApi(Build.VERSION_CODES.Q)
     private fun GLFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
         if (this != null) {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
index 98cd2bc5..3a54bb1 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
@@ -120,132 +120,133 @@
     private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
     private val mBufferTransform = BufferTransformer()
     private val mParentLayerTransform = android.graphics.Matrix()
+    private val mHolderCallback = object : SurfaceHolder.Callback2 {
+
+        private var mWidth = -1
+        private var mHeight = -1
+
+        private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+
+        private val mTransformResolver = BufferTransformHintResolver()
+
+        override fun surfaceCreated(p0: SurfaceHolder) {
+            // NO-OP
+        }
+
+        override fun surfaceChanged(
+            holder: SurfaceHolder,
+            format: Int,
+            width: Int,
+            height: Int
+        ) {
+            mWidth = width
+            mHeight = height
+            releaseInternal(true)
+            transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
+            inverse = mBufferTransform.invertBufferTransform(transformHint)
+            mBufferTransform.computeTransform(width, height, inverse)
+            updateMatrixTransform(width.toFloat(), height.toFloat(), inverse)
+
+            mPersistedCanvasRenderer = SingleBufferedCanvasRenderer.create<T>(
+                width,
+                height,
+                mBufferTransform,
+                mExecutor,
+                object : SingleBufferedCanvasRenderer.RenderCallbacks<T> {
+
+                    override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
+                        callback.onDrawFrontBufferedLayer(canvas, width, height, param)
+                    }
+
+                    @SuppressLint("WrongConstant")
+                    override fun onBufferReady(
+                        hardwareBuffer: HardwareBuffer,
+                        syncFenceCompat: SyncFenceCompat?
+                    ) {
+                        mPersistedCanvasRenderer?.isVisible = true
+                        mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
+                            val transaction = SurfaceControlCompat.Transaction()
+                                .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE)
+                                .setBuffer(
+                                    frontBufferSurfaceControl,
+                                    hardwareBuffer,
+                                    syncFenceCompat
+                                )
+                                .setVisibility(frontBufferSurfaceControl, true)
+                                .reparent(frontBufferSurfaceControl, mParentSurfaceControl)
+                            if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+                                transaction.setBufferTransform(
+                                    frontBufferSurfaceControl,
+                                    inverse
+                                )
+                            }
+                            callback.onFrontBufferedLayerRenderComplete(
+                                frontBufferSurfaceControl, transaction)
+                            transaction.commit()
+                            syncFenceCompat?.close()
+                        }
+                    }
+                })
+
+            val parentSurfaceControl = SurfaceControlCompat.Builder()
+                .setParent(surfaceView)
+                .setName("MultiBufferedLayer")
+                .build()
+                .apply {
+                    // SurfaceControl is not visible by default so make it visible right
+                    // after creation
+                    SurfaceControlCompat.Transaction()
+                        .setVisibility(this, true)
+                        .commit()
+                }
+
+            val multiBufferNode = RenderNode("MultiBufferNode").apply {
+                setPosition(0, 0, mBufferTransform.glWidth, mBufferTransform.glHeight)
+                mMultiBufferNode = this
+            }
+            mMultiBufferedCanvasRenderer = MultiBufferedCanvasRenderer(
+                multiBufferNode,
+                mBufferTransform.glWidth,
+                mBufferTransform.glHeight
+            )
+
+            mFrontBufferSurfaceControl = SurfaceControlCompat.Builder()
+                .setParent(parentSurfaceControl)
+                .setName("FrontBufferedLayer")
+                .build()
+
+            mParentSurfaceControl = parentSurfaceControl
+        }
+
+        override fun surfaceDestroyed(p0: SurfaceHolder) {
+            releaseInternal(true)
+        }
+
+        override fun surfaceRedrawNeeded(holder: SurfaceHolder) {
+            val latch = CountDownLatch(1)
+            surfaceRedrawNeededAsync(holder) {
+                latch.countDown()
+            }
+            latch.await()
+        }
+
+        override fun surfaceRedrawNeededAsync(
+            holder: SurfaceHolder,
+            drawingFinished: Runnable
+        ) {
+            val renderer = mMultiBufferedCanvasRenderer
+            if (renderer != null) {
+                renderer.renderFrame(mExecutor) { buffer ->
+                    setParentSurfaceControlBuffer(buffer, drawingFinished)
+                }
+            } else {
+                drawingFinished.run()
+            }
+        }
+    }
 
     init {
-        surfaceView.holder.addCallback(object : SurfaceHolder.Callback2 {
-
-            private var mWidth = -1
-            private var mHeight = -1
-
-            private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
-
-            private val mTransformResolver = BufferTransformHintResolver()
-
-            override fun surfaceCreated(p0: SurfaceHolder) {
-                // NO-OP
-            }
-
-            override fun surfaceChanged(
-                holder: SurfaceHolder,
-                format: Int,
-                width: Int,
-                height: Int
-            ) {
-                mWidth = width
-                mHeight = height
-                releaseInternal(true)
-                transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
-                inverse = mBufferTransform.invertBufferTransform(transformHint)
-                mBufferTransform.computeTransform(width, height, inverse)
-                updateMatrixTransform(width.toFloat(), height.toFloat(), inverse)
-
-                mPersistedCanvasRenderer = SingleBufferedCanvasRenderer.create<T>(
-                    width,
-                    height,
-                    mBufferTransform,
-                    mExecutor,
-                    object : SingleBufferedCanvasRenderer.RenderCallbacks<T> {
-
-                        override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
-                            callback.onDrawFrontBufferedLayer(canvas, width, height, param)
-                        }
-
-                        @SuppressLint("WrongConstant")
-                        override fun onBufferReady(
-                            hardwareBuffer: HardwareBuffer,
-                            syncFenceCompat: SyncFenceCompat?
-                        ) {
-                            mPersistedCanvasRenderer?.isVisible = true
-                            mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
-                                val transaction = SurfaceControlCompat.Transaction()
-                                    .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE)
-                                    .setBuffer(
-                                        frontBufferSurfaceControl,
-                                        hardwareBuffer,
-                                        syncFenceCompat
-                                    )
-                                    .setVisibility(frontBufferSurfaceControl, true)
-                                    .reparent(frontBufferSurfaceControl, mParentSurfaceControl)
-                                if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
-                                    transaction.setBufferTransform(
-                                        frontBufferSurfaceControl,
-                                        inverse
-                                    )
-                                }
-                                callback.onFrontBufferedLayerRenderComplete(
-                                    frontBufferSurfaceControl, transaction)
-                                transaction.commit()
-                                syncFenceCompat?.close()
-                            }
-                        }
-                    })
-
-                val parentSurfaceControl = SurfaceControlCompat.Builder()
-                    .setParent(surfaceView)
-                    .setName("MultiBufferedLayer")
-                    .build()
-                    .apply {
-                        // SurfaceControl is not visible by default so make it visible right
-                        // after creation
-                        SurfaceControlCompat.Transaction()
-                            .setVisibility(this, true)
-                            .commit()
-                    }
-
-                val multiBufferNode = RenderNode("MultiBufferNode").apply {
-                    setPosition(0, 0, mBufferTransform.glWidth, mBufferTransform.glHeight)
-                    mMultiBufferNode = this
-                }
-                mMultiBufferedCanvasRenderer = MultiBufferedCanvasRenderer(
-                    multiBufferNode,
-                    mBufferTransform.glWidth,
-                    mBufferTransform.glHeight
-                )
-
-                mFrontBufferSurfaceControl = SurfaceControlCompat.Builder()
-                    .setParent(parentSurfaceControl)
-                    .setName("FrontBufferedLayer")
-                    .build()
-
-                mParentSurfaceControl = parentSurfaceControl
-            }
-
-            override fun surfaceDestroyed(p0: SurfaceHolder) {
-                releaseInternal(true)
-            }
-
-            override fun surfaceRedrawNeeded(holder: SurfaceHolder) {
-                val latch = CountDownLatch(1)
-                surfaceRedrawNeededAsync(holder) {
-                    latch.countDown()
-                }
-                latch.await()
-            }
-
-            override fun surfaceRedrawNeededAsync(
-                holder: SurfaceHolder,
-                drawingFinished: Runnable
-            ) {
-                val renderer = mMultiBufferedCanvasRenderer
-                if (renderer != null) {
-                    renderer.renderFrame(mExecutor) { buffer ->
-                        setParentSurfaceControlBuffer(buffer, drawingFinished)
-                    }
-                } else {
-                    drawingFinished.run()
-                }
-            }
-        })
+        surfaceView.holder.addCallback(mHolderCallback)
     }
 
     private inline fun RenderNode.record(block: (canvas: Canvas) -> Unit): RenderNode {
@@ -480,6 +481,7 @@
     @JvmOverloads
     fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
         if (!mIsReleased) {
+            surfaceView.holder.removeCallback(mHolderCallback)
             releaseInternal(cancelPending) {
                 onReleaseComplete?.invoke()
                 mExecutor.shutdown()
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
index af54b29..65a5a40 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
@@ -35,6 +35,7 @@
 import androidx.hardware.SyncFenceCompat
 import androidx.opengl.EGLExt.Companion.EGL_ANDROID_NATIVE_FENCE_SYNC
 import androidx.opengl.EGLExt.Companion.EGL_KHR_FENCE_SYNC
+import java.lang.IllegalStateException
 import java.util.concurrent.ConcurrentLinkedQueue
 import java.util.concurrent.Executors
 
@@ -288,6 +289,11 @@
         } else {
             // ... otherwise use the [GLRenderer] that is being provided for us
             mIsManagingGLRenderer = false
+            if (!glRenderer.isRunning()) {
+                throw IllegalStateException("The provided GLRenderer must be running prior to " +
+                    "creation of GLFrontBufferedRenderer, " +
+                    "did you forget to call GLRenderer#start()?")
+            }
             glRenderer
         }
         renderer.registerEGLContextCallback(mContextCallbacks)
@@ -496,38 +502,27 @@
      * release SurfaceControl instances
      */
     internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
-        // Wrap the callback into a separate lambda to ensure it is invoked only after
-        // both the front and multi buffered layer target renderers are detached
-        var callbackCount = 0
-        var expectedCount = 0
-        if (mFrontBufferedRenderTarget?.isAttached() == true) {
-            expectedCount++
-        }
+        // GLRenderer processes requests in order on a single thread. So detach the corresponding
+        // render targets then queue a request to teardown all resources
+        mFrontBufferedRenderTarget?.detach(cancelPending)
+        mMultiBufferedLayerRenderTarget?.detach(cancelPending)
 
-        if (mMultiBufferedLayerRenderTarget?.isAttached() == true) {
-            expectedCount++
-        }
         val frontBufferedLayerSurfaceControl = mFrontBufferedLayerSurfaceControl
-        val wrappedCallback: (GLRenderer.RenderTarget) -> Unit = {
-            callbackCount++
-            if (callbackCount >= expectedCount) {
-                mBufferPool?.let { releaseBuffers(it) }
-                clearParamQueues()
-
-                frontBufferedLayerSurfaceControl?.let {
-                    val transaction = SurfaceControlCompat.Transaction()
-                        .reparent(it, null)
-                    mParentRenderLayer.release(transaction)
-                    transaction.commit()
-                    it.release()
-                }
-
-                onReleaseComplete?.invoke()
-            }
-        }
         mFrontBufferedLayerSurfaceControl = null
-        mFrontBufferedRenderTarget?.detach(cancelPending, wrappedCallback)
-        mMultiBufferedLayerRenderTarget?.detach(cancelPending, wrappedCallback)
+        mGLRenderer.execute {
+            mBufferPool?.let { releaseBuffers(it) }
+            clearParamQueues()
+
+            val transaction = SurfaceControlCompat.Transaction()
+            if (frontBufferedLayerSurfaceControl != null) {
+                transaction.reparent(frontBufferedLayerSurfaceControl, null)
+            }
+            mParentRenderLayer.release(transaction)
+            transaction.commit()
+            frontBufferedLayerSurfaceControl?.release()
+
+            onReleaseComplete?.invoke()
+        }
         mFrontBufferedRenderTarget = null
         mMultiBufferedLayerRenderTarget = null
         mWidth = -1
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
index b2407a3..ad99c0c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
@@ -48,36 +48,38 @@
 
     private val mTransformResolver = BufferTransformHintResolver()
 
-    private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
-    private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
-    init {
-        surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+    private var mTransformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+    private var mInverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+    private val mHolderCallback = object : SurfaceHolder.Callback {
 
-            override fun surfaceCreated(holder: SurfaceHolder) {
-                // NO-OP wait on surfaceChanged callback
-            }
+        override fun surfaceCreated(holder: SurfaceHolder) {
+            // NO-OP wait on surfaceChanged callback
+        }
 
-            override fun surfaceChanged(
-                holder: SurfaceHolder,
-                format: Int,
-                width: Int,
-                height: Int
-            ) {
-                transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
-                inverse = mBufferTransform.invertBufferTransform(transformHint)
-                mBufferTransform.computeTransform(width, height, inverse)
-                mParentSurfaceControl?.release()
-                mParentSurfaceControl = createDoubleBufferedSurfaceControl()
-                mLayerCallback?.onSizeChanged(width, height)
-            }
+        override fun surfaceChanged(
+            holder: SurfaceHolder,
+            format: Int,
+            width: Int,
+            height: Int
+        ) {
+            mTransformHint = mTransformResolver.getBufferTransformHint(surfaceView)
+            mInverse = mBufferTransform.invertBufferTransform(mTransformHint)
+            mBufferTransform.computeTransform(width, height, mInverse)
+            mParentSurfaceControl?.release()
+            mParentSurfaceControl = createDoubleBufferedSurfaceControl()
+            mLayerCallback?.onSizeChanged(width, height)
+        }
 
-            override fun surfaceDestroyed(p0: SurfaceHolder) {
-                mLayerCallback?.onLayerDestroyed()
-            }
-        })
+        override fun surfaceDestroyed(p0: SurfaceHolder) {
+            mLayerCallback?.onLayerDestroyed()
+        }
     }
 
-    override fun getInverseBufferTransform(): Int = inverse
+    init {
+        surfaceView.holder.addCallback(mHolderCallback)
+    }
+
+    override fun getInverseBufferTransform(): Int = mInverse
 
     override fun getBufferWidth(): Int = mBufferTransform.glWidth
 
@@ -151,8 +153,8 @@
                             .setBuffer(sc, frameBuffer.hardwareBuffer, syncFenceCompat) {
                                 mLayerCallback?.getFrameBufferPool()?.release(frameBuffer)
                             }
-                        if (transformHint != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
-                            transaction.setBufferTransform(sc, inverse)
+                        if (mTransformHint != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+                            transaction.setBufferTransform(sc, mInverse)
                         }
 
                         renderLayerCallback.onMultiBufferedLayerRenderComplete(
@@ -197,8 +199,8 @@
         mRenderTarget?.requestRender()
     }
 
-    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     override fun release(transaction: SurfaceControlCompat.Transaction) {
+        surfaceView.holder.removeCallback(mHolderCallback)
         mParentSurfaceControl?.let {
             transaction.reparent(it, null)
             it.release()
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index 8a8757a..06b657b 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -35,10 +35,7 @@
     // Add dependencies here
     api("androidx.activity:activity:1.2.0")
     api("androidx.annotation:annotation:1.2.0")
-    bundleInside(project(
-            path: ":health:connect:connect-client-proto",
-            configuration: "export"
-    ))
+    bundleInside(project(path: ":health:connect:connect-client-proto", configuration: "export"))
     implementation(libs.guavaListenableFuture)
     implementation(libs.guavaAndroid)
     implementation(libs.kotlinCoroutinesAndroid)
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt
new file mode 100644
index 0000000..08aa1fc
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.units.Length
+import java.time.Instant
+
+/**
+ * Captures the time of a lap within an exercise session.
+ *
+ * <p>Each lap contains the start and end time and optional [Length] of the lap (e.g. pool
+ * length while swimming or a track lap while running). There may or may not be direct correlation
+ * with [ExerciseSegment] start and end times, e.g. [ExerciseSessionRecord] of type
+ * running without any segments can be divided into laps of different lengths.
+ *
+ * @see ExerciseSessionRecord
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class ExerciseLap(
+    public val startTime: Instant,
+    public val endTime: Instant,
+    /** Lap length in [Length] unit. Optional field. Valid range: 0-1000000 meters. */
+    public val length: Length? = null,
+) {
+    init {
+        require(startTime.isBefore(endTime)) { "startTime must be before endTime." }
+        if (length != null) {
+            require(length.inMeters in 0.0..1000000.0) { "length valid range: 0-1000000." }
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ExerciseLap) return false
+
+        if (startTime != other.startTime) return false
+        if (endTime != other.endTime) return false
+        if (length != other.length) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = 0
+        result = 31 * result + startTime.hashCode()
+        result = 31 * result + endTime.hashCode()
+        result = 31 * result + length.hashCode()
+        return result
+    }
+}
\ No newline at end of file
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
new file mode 100644
index 0000000..7608b6a
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.units.Length
+import java.time.Instant
+
+/**
+ * Captures a route associated with an exercise session a user does.
+ *
+ * Contains a sequence of location points, with timestamps, which do not have to be in order.
+ *
+ * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal and
+ * vertical accuracy.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class ExerciseRoute(route: List<Location>) {
+    public val route: List<Location> = route.sortedWith { a, b -> a.time.compareTo(b.time) }
+
+    init {
+        for (i in 0 until this.route.lastIndex) {
+            require(this.route[i].time.isBefore(this.route[i + 1].time))
+        }
+    }
+    internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
+        // startTime is inclusive, endTime is exclusive
+        return !route.first().time.isBefore(startTime) && route.last().time.isBefore(endTime)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ExerciseRoute) return false
+
+        return route == other.route
+    }
+
+    override fun hashCode(): Int {
+        return route.hashCode()
+    }
+
+    /**
+     * Represents a single location point recorded during an exercise.
+     *
+     * @param time The point in time when the location was recorded; Required field.
+     * @param latitude Latitude of the location point; Required field; Valid range [-90; 90]
+     * @param longitude Longitude of the location point; Required field; Valid range [-180; 180]
+     * @param altitude in [Length] unit. Optional field. Valid range: non-negative numbers.
+     * @param horizontalAccuracy in [Length] unit. Optional field. Valid range: non-negative
+     *   numbers.
+     * @param verticalAccuracy in [Length] unit. Optional field. Valid range: non-negative numbers.
+     * @see ExerciseRoute
+     */
+    public class Location(
+        val time: Instant,
+        val latitude: Double,
+        val longitude: Double,
+        val horizontalAccuracy: Length? = null,
+        val verticalAccuracy: Length? = null,
+        val altitude: Length? = null
+    ) {
+
+        companion object {
+            private const val MIN_LONGITUDE = -180.0
+            private const val MAX_LONGITUDE = 180.0
+            private const val MIN_LATITUDE = -90.0
+            private const val MAX_LATITUDE = 90.0
+        }
+
+        init {
+            latitude.requireNotLess(other = MIN_LATITUDE, name = "latitude")
+            latitude.requireNotMore(other = MAX_LATITUDE, name = "latitude")
+            longitude.requireNotLess(other = MIN_LONGITUDE, name = "longitude")
+            longitude.requireNotMore(other = MAX_LONGITUDE, name = "longitude")
+            horizontalAccuracy?.requireNotLess(
+                other = horizontalAccuracy.zero(),
+                name = "horizontalAccuracy"
+            )
+            verticalAccuracy?.requireNotLess(
+                other = verticalAccuracy.zero(),
+                name = "verticalAccuracy"
+            )
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Location) return false
+
+            if (time != other.time) return false
+            if (latitude != other.latitude) return false
+            if (longitude != other.longitude) return false
+            if (horizontalAccuracy != other.horizontalAccuracy) return false
+            if (verticalAccuracy != other.verticalAccuracy) return false
+            if (altitude != other.altitude) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = time.hashCode()
+            result = 31 * result + latitude.hashCode()
+            result = 31 * result + longitude.hashCode()
+            result = 31 * result + (horizontalAccuracy?.hashCode() ?: 0)
+            result = 31 * result + (verticalAccuracy?.hashCode() ?: 0)
+            result = 31 * result + (altitude?.hashCode() ?: 0)
+            return result
+        }
+    }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt
new file mode 100644
index 0000000..968aeec
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt
@@ -0,0 +1,350 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import java.time.Instant
+
+/**
+ * Represents particular exercise within an exercise session.
+ *
+ * <p>Each segment contains start and end time of the exercise, exercise type and optional number of
+ * repetitions.
+ *
+ * @see ExerciseSessionRecord
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class ExerciseSegment(
+    public val startTime: Instant,
+    public val endTime: Instant,
+    @property:ExerciseSegmentTypes public val segmentType: Int,
+    public val repetitions: Int = 0,
+) {
+    init {
+        require(startTime.isBefore(endTime)) { "startTime must be before endTime." }
+        require(repetitions >= 0) { "repetitions can not be negative." }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ExerciseSegment) return false
+
+        if (startTime != other.startTime) return false
+        if (endTime != other.endTime) return false
+        if (segmentType != other.segmentType) return false
+        if (repetitions != other.repetitions) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = 0
+        result = 31 * result + startTime.hashCode()
+        result = 31 * result + endTime.hashCode()
+        result = 31 * result + segmentType.hashCode()
+        result = 31 * result + repetitions.hashCode()
+        return result
+    }
+
+    internal fun isCompatibleWith(sessionType: Int): Boolean {
+        if (UNIVERSAL_SESSION_TYPES.contains(sessionType)) {
+            return true
+        }
+        if (UNIVERSAL_SEGMENTS.contains(segmentType)) {
+            return true
+        }
+        if (!SESSION_TO_SEGMENTS_MAPPING.contains(sessionType)) {
+            return false
+        }
+        return SESSION_TO_SEGMENTS_MAPPING[sessionType]!!.contains(segmentType)
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    companion object {
+        /** Next Id: 68. */
+        const val EXERCISE_SEGMENT_TYPE_UNKNOWN = 0
+        const val EXERCISE_SEGMENT_TYPE_ARM_CURL = 1
+        const val EXERCISE_SEGMENT_TYPE_BACK_EXTENSION = 2
+        const val EXERCISE_SEGMENT_TYPE_BALL_SLAM = 3
+        const val EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS = 4
+        const val EXERCISE_SEGMENT_TYPE_BENCH_PRESS = 5
+        const val EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP = 6
+        const val EXERCISE_SEGMENT_TYPE_BIKING = 7
+        const val EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY = 8
+        const val EXERCISE_SEGMENT_TYPE_BURPEE = 9
+        const val EXERCISE_SEGMENT_TYPE_CRUNCH = 10
+        const val EXERCISE_SEGMENT_TYPE_DEADLIFT = 11
+        const val EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION = 12
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM = 13
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM = 14
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE = 15
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE = 16
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW = 17
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM = 18
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM = 19
+        const val EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM = 20
+        const val EXERCISE_SEGMENT_TYPE_ELLIPTICAL = 21
+        const val EXERCISE_SEGMENT_TYPE_FORWARD_TWIST = 22
+        const val EXERCISE_SEGMENT_TYPE_FRONT_RAISE = 23
+        const val EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING = 24
+        const val EXERCISE_SEGMENT_TYPE_HIP_THRUST = 25
+        const val EXERCISE_SEGMENT_TYPE_HULA_HOOP = 26
+        const val EXERCISE_SEGMENT_TYPE_JUMPING_JACK = 27
+        const val EXERCISE_SEGMENT_TYPE_JUMP_ROPE = 28
+        const val EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING = 29
+        const val EXERCISE_SEGMENT_TYPE_LATERAL_RAISE = 30
+        const val EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN = 31
+        const val EXERCISE_SEGMENT_TYPE_LEG_CURL = 32
+        const val EXERCISE_SEGMENT_TYPE_LEG_EXTENSION = 33
+        const val EXERCISE_SEGMENT_TYPE_LEG_PRESS = 34
+        const val EXERCISE_SEGMENT_TYPE_LEG_RAISE = 35
+        const val EXERCISE_SEGMENT_TYPE_LUNGE = 36
+        const val EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER = 37
+        const val EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT = 38
+        const val EXERCISE_SEGMENT_TYPE_PAUSE = 39
+        const val EXERCISE_SEGMENT_TYPE_PILATES = 40
+        const val EXERCISE_SEGMENT_TYPE_PLANK = 41
+        const val EXERCISE_SEGMENT_TYPE_PULL_UP = 42
+        const val EXERCISE_SEGMENT_TYPE_PUNCH = 43
+        const val EXERCISE_SEGMENT_TYPE_REST = 44
+        const val EXERCISE_SEGMENT_TYPE_ROWING_MACHINE = 45
+        const val EXERCISE_SEGMENT_TYPE_RUNNING = 46
+        const val EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL = 47
+        const val EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS = 48
+        const val EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION = 49
+        const val EXERCISE_SEGMENT_TYPE_SIT_UP = 50
+        const val EXERCISE_SEGMENT_TYPE_SQUAT = 51
+        const val EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING = 52
+        const val EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE = 53
+        const val EXERCISE_SEGMENT_TYPE_STRETCHING = 54
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE = 55
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE = 56
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY = 57
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE = 58
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED = 59
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER = 60
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER = 61
+        const val EXERCISE_SEGMENT_TYPE_SWIMMING_POOL = 62
+        const val EXERCISE_SEGMENT_TYPE_UPPER_TWIST = 63
+        const val EXERCISE_SEGMENT_TYPE_WALKING = 64
+        const val EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING = 65
+        const val EXERCISE_SEGMENT_TYPE_WHEELCHAIR = 66
+        const val EXERCISE_SEGMENT_TYPE_YOGA = 67
+
+        internal val UNIVERSAL_SESSION_TYPES = setOf(
+            ExerciseSessionRecord.EXERCISE_TYPE_BOOT_CAMP,
+            ExerciseSessionRecord.EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+            ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT,
+        )
+
+        internal val UNIVERSAL_SEGMENTS = setOf(
+            EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT,
+            EXERCISE_SEGMENT_TYPE_PAUSE,
+            EXERCISE_SEGMENT_TYPE_REST,
+            EXERCISE_SEGMENT_TYPE_STRETCHING,
+            EXERCISE_SEGMENT_TYPE_UNKNOWN,
+        )
+
+        internal val EXERCISE_SEGMENTS = setOf(
+            EXERCISE_SEGMENT_TYPE_ARM_CURL,
+            EXERCISE_SEGMENT_TYPE_BACK_EXTENSION,
+            EXERCISE_SEGMENT_TYPE_BALL_SLAM,
+            EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+            EXERCISE_SEGMENT_TYPE_BENCH_PRESS,
+            EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP,
+            EXERCISE_SEGMENT_TYPE_BURPEE,
+            EXERCISE_SEGMENT_TYPE_CRUNCH,
+            EXERCISE_SEGMENT_TYPE_DEADLIFT,
+            EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM,
+            EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM,
+            EXERCISE_SEGMENT_TYPE_FORWARD_TWIST,
+            EXERCISE_SEGMENT_TYPE_FRONT_RAISE,
+            EXERCISE_SEGMENT_TYPE_HIP_THRUST,
+            EXERCISE_SEGMENT_TYPE_HULA_HOOP,
+            EXERCISE_SEGMENT_TYPE_JUMP_ROPE,
+            EXERCISE_SEGMENT_TYPE_JUMPING_JACK,
+            EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING,
+            EXERCISE_SEGMENT_TYPE_LATERAL_RAISE,
+            EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN,
+            EXERCISE_SEGMENT_TYPE_LEG_CURL,
+            EXERCISE_SEGMENT_TYPE_LEG_EXTENSION,
+            EXERCISE_SEGMENT_TYPE_LEG_PRESS,
+            EXERCISE_SEGMENT_TYPE_LEG_RAISE,
+            EXERCISE_SEGMENT_TYPE_LUNGE,
+            EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER,
+            EXERCISE_SEGMENT_TYPE_PLANK,
+            EXERCISE_SEGMENT_TYPE_PULL_UP,
+            EXERCISE_SEGMENT_TYPE_PUNCH,
+            EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS,
+            EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION,
+            EXERCISE_SEGMENT_TYPE_SIT_UP,
+            EXERCISE_SEGMENT_TYPE_SQUAT,
+            EXERCISE_SEGMENT_TYPE_UPPER_TWIST,
+            EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING
+        )
+        internal val SWIMMING_SEGMENTS = setOf(
+            EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE,
+            EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE,
+            EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE,
+            EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY,
+            EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED,
+            EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER
+        )
+
+        private val SESSION_TO_SEGMENTS_MAPPING = mapOf(
+            ExerciseSessionRecord.EXERCISE_TYPE_BIKING to setOf(EXERCISE_SEGMENT_TYPE_BIKING),
+            ExerciseSessionRecord.EXERCISE_TYPE_BIKING_STATIONARY to setOf(
+                EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS to EXERCISE_SEGMENTS,
+            ExerciseSessionRecord.EXERCISE_TYPE_ELLIPTICAL
+                to setOf(EXERCISE_SEGMENT_TYPE_ELLIPTICAL),
+            ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS to setOf(
+                EXERCISE_SEGMENT_TYPE_YOGA,
+                EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+                EXERCISE_SEGMENT_TYPE_PILATES,
+                EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_GYMNASTICS to EXERCISE_SEGMENTS,
+            ExerciseSessionRecord.EXERCISE_TYPE_HIKING to setOf(
+                EXERCISE_SEGMENT_TYPE_WALKING,
+                EXERCISE_SEGMENT_TYPE_WHEELCHAIR
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_PILATES to setOf(EXERCISE_SEGMENT_TYPE_PILATES),
+            ExerciseSessionRecord.EXERCISE_TYPE_ROWING_MACHINE to setOf(
+                EXERCISE_SEGMENT_TYPE_ROWING_MACHINE
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_RUNNING to setOf(
+                EXERCISE_SEGMENT_TYPE_RUNNING,
+                EXERCISE_SEGMENT_TYPE_WALKING
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_RUNNING_TREADMILL to setOf(
+                EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_STRENGTH_TRAINING to EXERCISE_SEGMENTS,
+            ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING to setOf(
+                EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING_MACHINE to setOf(
+                EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE
+            ),
+            ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER to buildSet {
+                add(EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER)
+                addAll(SWIMMING_SEGMENTS)
+            },
+            ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_POOL to buildSet {
+                add(EXERCISE_SEGMENT_TYPE_SWIMMING_POOL)
+                addAll(SWIMMING_SEGMENTS)
+            },
+            ExerciseSessionRecord.EXERCISE_TYPE_WALKING to setOf(EXERCISE_SEGMENT_TYPE_WALKING),
+            ExerciseSessionRecord.EXERCISE_TYPE_WHEELCHAIR
+                to setOf(EXERCISE_SEGMENT_TYPE_WHEELCHAIR),
+            ExerciseSessionRecord.EXERCISE_TYPE_WEIGHTLIFTING to EXERCISE_SEGMENTS,
+            ExerciseSessionRecord.EXERCISE_TYPE_YOGA to setOf(EXERCISE_SEGMENT_TYPE_YOGA),
+        )
+
+        /**
+         * List of supported segment types on Health Platform.
+         *
+         * @suppress
+         */
+        @Retention(AnnotationRetention.SOURCE)
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @IntDef(
+            value =
+            [
+                EXERCISE_SEGMENT_TYPE_UNKNOWN,
+                EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+                EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP,
+                EXERCISE_SEGMENT_TYPE_BIKING,
+                EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM,
+                EXERCISE_SEGMENT_TYPE_FORWARD_TWIST,
+                EXERCISE_SEGMENT_TYPE_ELLIPTICAL,
+                EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+                EXERCISE_SEGMENT_TYPE_PILATES,
+                EXERCISE_SEGMENT_TYPE_ROWING_MACHINE,
+                EXERCISE_SEGMENT_TYPE_RUNNING,
+                EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL,
+                EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING,
+                EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE,
+                EXERCISE_SEGMENT_TYPE_STRETCHING,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_POOL,
+                EXERCISE_SEGMENT_TYPE_UPPER_TWIST,
+                EXERCISE_SEGMENT_TYPE_WALKING,
+                EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING,
+                EXERCISE_SEGMENT_TYPE_WHEELCHAIR,
+                EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT,
+                EXERCISE_SEGMENT_TYPE_YOGA,
+                EXERCISE_SEGMENT_TYPE_ARM_CURL,
+                EXERCISE_SEGMENT_TYPE_BACK_EXTENSION,
+                EXERCISE_SEGMENT_TYPE_BALL_SLAM,
+                EXERCISE_SEGMENT_TYPE_BENCH_PRESS,
+                EXERCISE_SEGMENT_TYPE_BURPEE,
+                EXERCISE_SEGMENT_TYPE_CRUNCH,
+                EXERCISE_SEGMENT_TYPE_DEADLIFT,
+                EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION,
+                EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW,
+                EXERCISE_SEGMENT_TYPE_FRONT_RAISE,
+                EXERCISE_SEGMENT_TYPE_HIP_THRUST,
+                EXERCISE_SEGMENT_TYPE_HULA_HOOP,
+                EXERCISE_SEGMENT_TYPE_JUMPING_JACK,
+                EXERCISE_SEGMENT_TYPE_JUMP_ROPE,
+                EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING,
+                EXERCISE_SEGMENT_TYPE_LATERAL_RAISE,
+                EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN,
+                EXERCISE_SEGMENT_TYPE_LEG_CURL,
+                EXERCISE_SEGMENT_TYPE_LEG_EXTENSION,
+                EXERCISE_SEGMENT_TYPE_LEG_PRESS,
+                EXERCISE_SEGMENT_TYPE_LEG_RAISE,
+                EXERCISE_SEGMENT_TYPE_LUNGE,
+                EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER,
+                EXERCISE_SEGMENT_TYPE_PLANK,
+                EXERCISE_SEGMENT_TYPE_PULL_UP,
+                EXERCISE_SEGMENT_TYPE_PUNCH,
+                EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS,
+                EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION,
+                EXERCISE_SEGMENT_TYPE_SIT_UP,
+                EXERCISE_SEGMENT_TYPE_SQUAT,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED,
+                EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER,
+                EXERCISE_SEGMENT_TYPE_REST,
+                EXERCISE_SEGMENT_TYPE_PAUSE,
+            ]
+        )
+        annotation class ExerciseSegmentTypes
+    }
+}
\ No newline at end of file
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
index f2b31a1..c780c9c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
@@ -272,69 +272,69 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @IntDef(
         value =
-            [
-                EXERCISE_TYPE_BADMINTON,
-                EXERCISE_TYPE_BASEBALL,
-                EXERCISE_TYPE_BASKETBALL,
-                EXERCISE_TYPE_BIKING,
-                EXERCISE_TYPE_BIKING_STATIONARY,
-                EXERCISE_TYPE_BOOT_CAMP,
-                EXERCISE_TYPE_BOXING,
-                EXERCISE_TYPE_CALISTHENICS,
-                EXERCISE_TYPE_CRICKET,
-                EXERCISE_TYPE_DANCING,
-                EXERCISE_TYPE_ELLIPTICAL,
-                EXERCISE_TYPE_EXERCISE_CLASS,
-                EXERCISE_TYPE_FENCING,
-                EXERCISE_TYPE_FOOTBALL_AMERICAN,
-                EXERCISE_TYPE_FOOTBALL_AUSTRALIAN,
-                EXERCISE_TYPE_FRISBEE_DISC,
-                EXERCISE_TYPE_GOLF,
-                EXERCISE_TYPE_GUIDED_BREATHING,
-                EXERCISE_TYPE_GYMNASTICS,
-                EXERCISE_TYPE_HANDBALL,
-                EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
-                EXERCISE_TYPE_HIKING,
-                EXERCISE_TYPE_ICE_HOCKEY,
-                EXERCISE_TYPE_ICE_SKATING,
-                EXERCISE_TYPE_MARTIAL_ARTS,
-                EXERCISE_TYPE_PADDLING,
-                EXERCISE_TYPE_PARAGLIDING,
-                EXERCISE_TYPE_PILATES,
-                EXERCISE_TYPE_RACQUETBALL,
-                EXERCISE_TYPE_ROCK_CLIMBING,
-                EXERCISE_TYPE_ROLLER_HOCKEY,
-                EXERCISE_TYPE_ROWING,
-                EXERCISE_TYPE_ROWING_MACHINE,
-                EXERCISE_TYPE_RUGBY,
-                EXERCISE_TYPE_RUNNING,
-                EXERCISE_TYPE_RUNNING_TREADMILL,
-                EXERCISE_TYPE_SAILING,
-                EXERCISE_TYPE_SCUBA_DIVING,
-                EXERCISE_TYPE_SKATING,
-                EXERCISE_TYPE_SKIING,
-                EXERCISE_TYPE_SNOWBOARDING,
-                EXERCISE_TYPE_SNOWSHOEING,
-                EXERCISE_TYPE_SOCCER,
-                EXERCISE_TYPE_SOFTBALL,
-                EXERCISE_TYPE_SQUASH,
-                EXERCISE_TYPE_STAIR_CLIMBING,
-                EXERCISE_TYPE_STAIR_CLIMBING_MACHINE,
-                EXERCISE_TYPE_STRENGTH_TRAINING,
-                EXERCISE_TYPE_STRETCHING,
-                EXERCISE_TYPE_SURFING,
-                EXERCISE_TYPE_SWIMMING_OPEN_WATER,
-                EXERCISE_TYPE_SWIMMING_POOL,
-                EXERCISE_TYPE_TABLE_TENNIS,
-                EXERCISE_TYPE_TENNIS,
-                EXERCISE_TYPE_VOLLEYBALL,
-                EXERCISE_TYPE_WALKING,
-                EXERCISE_TYPE_WATER_POLO,
-                EXERCISE_TYPE_WEIGHTLIFTING,
-                EXERCISE_TYPE_WHEELCHAIR,
-                EXERCISE_TYPE_OTHER_WORKOUT,
-                EXERCISE_TYPE_YOGA,
-            ]
+        [
+            EXERCISE_TYPE_BADMINTON,
+            EXERCISE_TYPE_BASEBALL,
+            EXERCISE_TYPE_BASKETBALL,
+            EXERCISE_TYPE_BIKING,
+            EXERCISE_TYPE_BIKING_STATIONARY,
+            EXERCISE_TYPE_BOOT_CAMP,
+            EXERCISE_TYPE_BOXING,
+            EXERCISE_TYPE_CALISTHENICS,
+            EXERCISE_TYPE_CRICKET,
+            EXERCISE_TYPE_DANCING,
+            EXERCISE_TYPE_ELLIPTICAL,
+            EXERCISE_TYPE_EXERCISE_CLASS,
+            EXERCISE_TYPE_FENCING,
+            EXERCISE_TYPE_FOOTBALL_AMERICAN,
+            EXERCISE_TYPE_FOOTBALL_AUSTRALIAN,
+            EXERCISE_TYPE_FRISBEE_DISC,
+            EXERCISE_TYPE_GOLF,
+            EXERCISE_TYPE_GUIDED_BREATHING,
+            EXERCISE_TYPE_GYMNASTICS,
+            EXERCISE_TYPE_HANDBALL,
+            EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+            EXERCISE_TYPE_HIKING,
+            EXERCISE_TYPE_ICE_HOCKEY,
+            EXERCISE_TYPE_ICE_SKATING,
+            EXERCISE_TYPE_MARTIAL_ARTS,
+            EXERCISE_TYPE_PADDLING,
+            EXERCISE_TYPE_PARAGLIDING,
+            EXERCISE_TYPE_PILATES,
+            EXERCISE_TYPE_RACQUETBALL,
+            EXERCISE_TYPE_ROCK_CLIMBING,
+            EXERCISE_TYPE_ROLLER_HOCKEY,
+            EXERCISE_TYPE_ROWING,
+            EXERCISE_TYPE_ROWING_MACHINE,
+            EXERCISE_TYPE_RUGBY,
+            EXERCISE_TYPE_RUNNING,
+            EXERCISE_TYPE_RUNNING_TREADMILL,
+            EXERCISE_TYPE_SAILING,
+            EXERCISE_TYPE_SCUBA_DIVING,
+            EXERCISE_TYPE_SKATING,
+            EXERCISE_TYPE_SKIING,
+            EXERCISE_TYPE_SNOWBOARDING,
+            EXERCISE_TYPE_SNOWSHOEING,
+            EXERCISE_TYPE_SOCCER,
+            EXERCISE_TYPE_SOFTBALL,
+            EXERCISE_TYPE_SQUASH,
+            EXERCISE_TYPE_STAIR_CLIMBING,
+            EXERCISE_TYPE_STAIR_CLIMBING_MACHINE,
+            EXERCISE_TYPE_STRENGTH_TRAINING,
+            EXERCISE_TYPE_STRETCHING,
+            EXERCISE_TYPE_SURFING,
+            EXERCISE_TYPE_SWIMMING_OPEN_WATER,
+            EXERCISE_TYPE_SWIMMING_POOL,
+            EXERCISE_TYPE_TABLE_TENNIS,
+            EXERCISE_TYPE_TENNIS,
+            EXERCISE_TYPE_VOLLEYBALL,
+            EXERCISE_TYPE_WALKING,
+            EXERCISE_TYPE_WATER_POLO,
+            EXERCISE_TYPE_WEIGHTLIFTING,
+            EXERCISE_TYPE_WHEELCHAIR,
+            EXERCISE_TYPE_OTHER_WORKOUT,
+            EXERCISE_TYPE_YOGA,
+        ]
     )
     annotation class ExerciseTypes
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt
new file mode 100644
index 0000000..48c7202
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.health.connect.client.units.meters
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ExerciseLapTest {
+    @Test
+    fun validLap_equals() {
+        assertThat(
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678L),
+                length = 1.meters
+            )
+        ).isEqualTo(
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678L),
+                length = 1.meters
+            )
+        )
+    }
+
+    @Test
+    fun invalidTimes_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(1234L),
+            )
+        }
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(5678L),
+                endTime = Instant.ofEpochMilli(1234L),
+            )
+        }
+    }
+
+    @Test
+    fun invalidLength_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678),
+                length = (-1).meters,
+            )
+        }
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseLap(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678),
+                length = 1_000_001.meters,
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
new file mode 100644
index 0000000..a2edd79
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.health.connect.client.units.Length
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlin.test.assertFailsWith
+import org.junit.Test
+
+class ExerciseRouteTest {
+
+    @Test
+    fun validLocation_equals() {
+        assertThat(
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1234L),
+                latitude = 34.5,
+                longitude = -34.5,
+                horizontalAccuracy = Length.meters(0.4),
+                verticalAccuracy = Length.meters(1.3),
+                altitude = Length.meters(23.4)
+            )
+        )
+            .isEqualTo(
+                ExerciseRoute.Location(
+                    time = Instant.ofEpochMilli(1234L),
+                    latitude = 34.5,
+                    longitude = -34.5,
+                    horizontalAccuracy = Length.meters(0.4),
+                    verticalAccuracy = Length.meters(1.3),
+                    altitude = Length.meters(23.4)
+                )
+            )
+    }
+
+    @Test
+    fun invalidLatitudeAndLongitude_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1234L),
+                latitude = -91.0,
+                longitude = -34.5,
+                horizontalAccuracy = Length.meters(0.4),
+                verticalAccuracy = Length.meters(1.3),
+                altitude = Length.meters(23.4)
+            )
+        }
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1234L),
+                latitude = 34.5,
+                longitude = 189.5,
+                horizontalAccuracy = Length.meters(0.4),
+                verticalAccuracy = Length.meters(1.3),
+                altitude = Length.meters(23.4)
+            )
+        }
+    }
+
+    @Test
+    fun emptyRoute() {
+        assertThat(ExerciseRoute(listOf())).isEqualTo(ExerciseRoute(listOf()))
+    }
+
+    @Test
+    fun nonEmptyRoute() {
+        val location1 =
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1234L),
+                latitude = 34.5,
+                longitude = -34.5,
+                horizontalAccuracy = Length.meters(0.4),
+                verticalAccuracy = Length.meters(1.3),
+                altitude = Length.meters(23.4)
+            )
+        val location2 =
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(2345L),
+                latitude = 34.8,
+                longitude = -34.8,
+            )
+        assertThat(ExerciseRoute(listOf(location1, location2)))
+            .isEqualTo(ExerciseRoute(listOf(location1, location2)))
+    }
+
+    @Test
+    fun locationTimeOverlap_throws() {
+        val location1 = ExerciseRoute.Location(
+            time = Instant.ofEpochMilli(1234L),
+            latitude = 34.5,
+            longitude = -34.5,
+        )
+        val location2 =
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1234L),
+                latitude = 34.8,
+                longitude = -34.8,
+            )
+        assertFailsWith<IllegalArgumentException> { ExerciseRoute(listOf(location1, location2)) }
+    }
+
+    @Test
+    fun locationTime_sorted() {
+        val location1 = ExerciseRoute.Location(
+            time = Instant.ofEpochMilli(1236L),
+            latitude = 34.5,
+            longitude = -34.5,
+        )
+        val location2 =
+            ExerciseRoute.Location(
+                time = Instant.ofEpochMilli(1235L),
+                latitude = 34.8,
+                longitude = -34.8,
+            )
+        assertThat(ExerciseRoute(listOf(location1, location2)).route).isEqualTo(
+            listOf(location2, location1)
+        )
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSegmentTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSegmentTest.kt
new file mode 100644
index 0000000..b1d35f8
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSegmentTest.kt
@@ -0,0 +1,284 @@
+/*
+ * 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.health.connect.client.records
+
+import androidx.health.connect.client.records.ExerciseSegment.Companion.EXERCISE_SEGMENTS
+import androidx.health.connect.client.records.ExerciseSegment.Companion.SWIMMING_SEGMENTS
+import androidx.health.connect.client.records.ExerciseSegment.Companion.UNIVERSAL_SEGMENTS
+import androidx.health.connect.client.records.ExerciseSegment.Companion.UNIVERSAL_SESSION_TYPES
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlin.reflect.typeOf
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ExerciseSegmentTest {
+    private val allSegmentTypes =
+        ExerciseSegment.Companion::class
+            .members
+            .asSequence()
+            .filter { it.name.startsWith("EXERCISE_SEGMENT_TYPE_") }
+            .filter { it.returnType == typeOf<Int>() }
+            .map { it.call(ExerciseSegment.Companion) as Int }
+            .toSet()
+
+    private val allSessionTypes =
+        ExerciseSessionRecord.Companion::class
+            .members
+            .asSequence()
+            .filter { it.name.startsWith("EXERCISE_TYPE_") }
+            .filter { it.returnType == typeOf<Int>() }
+            .map { it.call(ExerciseSegment.Companion) as Int }
+            .toSet()
+
+    private val exerciseSessionTypes = setOf(
+        ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS,
+        ExerciseSessionRecord.EXERCISE_TYPE_GYMNASTICS,
+        ExerciseSessionRecord.EXERCISE_TYPE_STRENGTH_TRAINING,
+        ExerciseSessionRecord.EXERCISE_TYPE_WEIGHTLIFTING
+    )
+
+    private val swimmingSessionTypes = setOf(
+        ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_POOL,
+        ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER,
+    )
+
+    private val hikingSegments = setOf(
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_WALKING,
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_WHEELCHAIR
+    )
+
+    private val runningSegments = setOf(
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING,
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_WALKING
+    )
+
+    private val exerciseClassSegments = setOf(
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_PILATES,
+        ExerciseSegment.EXERCISE_SEGMENT_TYPE_YOGA,
+    )
+
+    private val sameSessionAndSegmentTypePairs = mapOf(
+        ExerciseSessionRecord.EXERCISE_TYPE_BIKING to ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING,
+        ExerciseSessionRecord.EXERCISE_TYPE_BIKING_STATIONARY
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+        ExerciseSessionRecord.EXERCISE_TYPE_ELLIPTICAL
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_ELLIPTICAL,
+        ExerciseSessionRecord.EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+        ExerciseSessionRecord.EXERCISE_TYPE_PILATES
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_PILATES,
+        ExerciseSessionRecord.EXERCISE_TYPE_ROWING_MACHINE
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_ROWING_MACHINE,
+        ExerciseSessionRecord.EXERCISE_TYPE_RUNNING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING,
+        ExerciseSessionRecord.EXERCISE_TYPE_RUNNING_TREADMILL
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL,
+        ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING,
+        ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING_MACHINE
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE,
+        ExerciseSessionRecord.EXERCISE_TYPE_STRETCHING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_STRETCHING,
+        ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER,
+        ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_POOL
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_POOL,
+        ExerciseSessionRecord.EXERCISE_TYPE_WALKING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_WALKING,
+        ExerciseSessionRecord.EXERCISE_TYPE_WEIGHTLIFTING
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING,
+        ExerciseSessionRecord.EXERCISE_TYPE_WHEELCHAIR
+            to ExerciseSegment.EXERCISE_SEGMENT_TYPE_WHEELCHAIR,
+        ExerciseSessionRecord.EXERCISE_TYPE_YOGA to ExerciseSegment.EXERCISE_SEGMENT_TYPE_YOGA,
+    )
+
+    @Test
+    fun validSegment_equals() {
+        assertThat(
+            ExerciseSegment(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678L),
+                segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK,
+                repetitions = 10,
+            )
+        ).isEqualTo(
+            ExerciseSegment(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678L),
+                segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK,
+                repetitions = 10,
+            )
+        )
+    }
+
+    @Test
+    fun invalidTimes_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseSegment(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(1234L),
+                segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK,
+            )
+        }
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseSegment(
+                startTime = Instant.ofEpochMilli(5678L),
+                endTime = Instant.ofEpochMilli(1234L),
+                segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK,
+            )
+        }
+    }
+
+    @Test
+    fun invalidReps_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ExerciseSegment(
+                startTime = Instant.ofEpochMilli(1234L),
+                endTime = Instant.ofEpochMilli(5678),
+                segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK,
+                repetitions = -1,
+            )
+        }
+    }
+
+    @Test
+    fun isCompatible_universalSession_acceptsEverything() {
+        UNIVERSAL_SESSION_TYPES.forEach { sessionType ->
+            allSegmentTypes.forEach { segmentType ->
+                assertCompatibility(sessionType, segmentType)
+            }
+        }
+    }
+
+    @Test
+    fun isCompatible_universalSegment_fitsInEverything() {
+        allSessionTypes.forEach { sessionType ->
+            UNIVERSAL_SEGMENTS.forEach { segmentType ->
+                assertCompatibility(sessionType, segmentType)
+            }
+        }
+    }
+
+    @Test
+    fun isCompatible_sameSessionAndSegmentType_returnsTrue() {
+        sameSessionAndSegmentTypePairs.forEach { (sessionType, segmentType) ->
+            assertCompatibility(sessionType, segmentType)
+        }
+    }
+
+    @Test
+    fun isCompatible_genericExerciseSessions_acceptGenericExerciseSegments() {
+        exerciseSessionTypes.forEach { sessionType ->
+            EXERCISE_SEGMENTS.forEach { segmentType ->
+                assertCompatibility(sessionType, segmentType)
+            }
+        }
+    }
+
+    @Test
+    fun isCompatible_swimmingSessions_acceptSwimmingSegments() {
+        swimmingSessionTypes.forEach { sessionType ->
+            SWIMMING_SEGMENTS.forEach { segmentType ->
+                assertCompatibility(sessionType, segmentType)
+            }
+        }
+    }
+
+    @Test
+    fun isCompatible_exerciseClassSession_acceptClassSegments() {
+        assertCompatibility(
+            ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
+            ExerciseSegment.EXERCISE_SEGMENT_TYPE_YOGA
+        )
+        assertCompatibility(
+            ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
+            ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY
+        )
+        assertCompatibility(
+            ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
+            ExerciseSegment.EXERCISE_SEGMENT_TYPE_PILATES
+        )
+        assertCompatibility(
+            ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
+            ExerciseSegment.EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
+        )
+    }
+
+    @Test
+    fun isCompatible_hikingSession_acceptWalkingAndWheelchair() {
+        hikingSegments.forEach { segmentType ->
+            assertCompatibility(ExerciseSessionRecord.EXERCISE_TYPE_HIKING, segmentType)
+        }
+    }
+
+    @Test
+    fun isCompatible_runningSession_acceptRunningAndWalking() {
+        runningSegments.forEach { segmentType ->
+            assertCompatibility(ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, segmentType)
+        }
+    }
+
+    @Test
+    fun isCompatible_allOtherCombinations_returnsFalse() {
+        allSessionTypes.filter { !UNIVERSAL_SESSION_TYPES.contains(it) }.forEach { sessionType ->
+            allSegmentTypes.asSequence().filter { !UNIVERSAL_SEGMENTS.contains(it) }
+                .filter { !(sameSessionAndSegmentTypePairs[sessionType]?.equals(it) ?: false) }
+                .filter {
+                    !(exerciseSessionTypes.contains(sessionType) && EXERCISE_SEGMENTS.contains(it))
+                }
+                .filter {
+                    !(swimmingSessionTypes.contains(sessionType) && SWIMMING_SEGMENTS.contains(it))
+                }
+                .filter {
+                    !(sessionType == ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS &&
+                        exerciseClassSegments.contains(it))
+                }
+                .filter {
+                    !(sessionType == ExerciseSessionRecord.EXERCISE_TYPE_HIKING &&
+                        hikingSegments.contains(it))
+                }
+                .filter {
+                    !(sessionType == ExerciseSessionRecord.EXERCISE_TYPE_RUNNING &&
+                        runningSegments.contains(it))
+                }.toList()
+                .forEach { segmentType -> assertCompatibility(sessionType, segmentType, false) }
+        }
+    }
+
+    private fun assertCompatibility(
+        sessionType: Int,
+        segmentType: Int,
+        isCompatible: Boolean = true
+    ) {
+        assertEquals(
+            expected = isCompatible,
+            actual = ExerciseSegment(
+                startTime = Instant.ofEpochMilli(1),
+                endTime = Instant.ofEpochMilli(2),
+                segmentType = segmentType
+            ).isCompatibleWith(sessionType),
+            message = "$sessionType and $segmentType is not compatible"
+        )
+    }
+}
\ No newline at end of file
diff --git a/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl b/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
index f7888a7..96b70225 100644
--- a/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
+++ b/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
@@ -36,9 +36,9 @@
 interface IJsSandboxConsoleCallback {
   void consoleMessage(int contextGroupId, int level, String message, String source, int line, int column, String trace) = 0;
   void consoleClear(int contextGroupId) = 1;
-  const int CONSOLE_MESSAGE_LEVEL_LOG = (1 << 0);
-  const int CONSOLE_MESSAGE_LEVEL_DEBUG = (1 << 1);
-  const int CONSOLE_MESSAGE_LEVEL_INFO = (1 << 2);
-  const int CONSOLE_MESSAGE_LEVEL_ERROR = (1 << 3);
-  const int CONSOLE_MESSAGE_LEVEL_WARNING = (1 << 4);
+  const int CONSOLE_MESSAGE_LEVEL_LOG = (1 << 0) /* 1 */;
+  const int CONSOLE_MESSAGE_LEVEL_DEBUG = (1 << 1) /* 2 */;
+  const int CONSOLE_MESSAGE_LEVEL_INFO = (1 << 2) /* 4 */;
+  const int CONSOLE_MESSAGE_LEVEL_ERROR = (1 << 3) /* 8 */;
+  const int CONSOLE_MESSAGE_LEVEL_WARNING = (1 << 4) /* 16 */;
 }
diff --git a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
index 553e919..8a7e238 100644
--- a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
+++ b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -957,7 +958,8 @@
             }
 
             @Override
-            public void onConsoleMessage(JavaScriptConsoleCallback.ConsoleMessage message) {
+            public void onConsoleMessage(
+                    @NonNull JavaScriptConsoleCallback.ConsoleMessage message) {
                 synchronized (mLock) {
                     mMessages.append(message.toString()).append("\n");
                 }
@@ -1123,7 +1125,7 @@
             CountDownLatch latch = new CountDownLatch(1);
             jsIsolate.setConsoleCallback(new JavaScriptConsoleCallback() {
                 @Override
-                public void onConsoleMessage(ConsoleMessage message) {}
+                public void onConsoleMessage(@NonNull ConsoleMessage message) {}
 
                 @Override
                 public void onConsoleClear() {
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java
index 75ed67b..b7acd25 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java
@@ -162,6 +162,7 @@
             return mTrace;
         }
 
+        @NonNull
         @Override
         public String toString() {
             return new StringBuilder()
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
index f1a563b..00afeb8 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
@@ -37,6 +37,7 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -89,15 +90,17 @@
 
     private class IJsSandboxIsolateSyncCallbackStubWrapper extends
             IJsSandboxIsolateSyncCallback.Stub {
+        @NonNull
         private CallbackToFutureAdapter.Completer<String> mCompleter;
 
         IJsSandboxIsolateSyncCallbackStubWrapper(
-                CallbackToFutureAdapter.Completer<String> completer) {
+                @NonNull CallbackToFutureAdapter.Completer<String> completer) {
             mCompleter = completer;
         }
 
         @Override
         public void reportResultWithFd(AssetFileDescriptor afd) {
+            Objects.requireNonNull(afd);
             mJsSandbox.mThreadPoolTaskExecutor.execute(
                     () -> {
                         String result;
@@ -128,6 +131,7 @@
 
         @Override
         public void reportErrorWithFd(@ExecutionErrorTypes int type, AssetFileDescriptor afd) {
+            Objects.requireNonNull(afd);
             mJsSandbox.mThreadPoolTaskExecutor.execute(
                     () -> {
                         String error;
@@ -147,29 +151,36 @@
     }
 
     private class IJsSandboxIsolateCallbackStubWrapper extends IJsSandboxIsolateCallback.Stub {
+        @NonNull
         private CallbackToFutureAdapter.Completer<String> mCompleter;
 
-        IJsSandboxIsolateCallbackStubWrapper(CallbackToFutureAdapter.Completer<String> completer) {
+        IJsSandboxIsolateCallbackStubWrapper(
+                @NonNull CallbackToFutureAdapter.Completer<String> completer) {
             mCompleter = completer;
         }
 
         @Override
         public void reportResult(String result) {
+            Objects.requireNonNull(result);
             handleEvaluationResult(mCompleter, result);
         }
 
         @Override
         public void reportError(@ExecutionErrorTypes int type, String error) {
+            Objects.requireNonNull(error);
             handleEvaluationError(mCompleter, type, error);
         }
     }
 
     private static final class JsSandboxConsoleCallbackRelay
             extends IJsSandboxConsoleCallback.Stub {
+        @NonNull
         private final Executor mExecutor;
+        @NonNull
         private final JavaScriptConsoleCallback mCallback;
 
-        JsSandboxConsoleCallbackRelay(Executor executor, JavaScriptConsoleCallback callback) {
+        JsSandboxConsoleCallbackRelay(@NonNull Executor executor,
+                @NonNull JavaScriptConsoleCallback callback) {
             mExecutor = executor;
             mCallback = callback;
         }
@@ -185,12 +196,8 @@
                         throw new IllegalArgumentException(
                                 "invalid console level " + level + " provided by isolate");
                     }
-                    if (message == null) {
-                        throw new IllegalArgumentException("null message provided by isolate");
-                    }
-                    if (source == null) {
-                        throw new IllegalArgumentException("null source provided by isolate");
-                    }
+                    Objects.requireNonNull(message);
+                    Objects.requireNonNull(source);
                     mCallback.onConsoleMessage(
                             new JavaScriptConsoleCallback.ConsoleMessage(
                                 level, message, source, line, column, trace));
@@ -215,8 +222,8 @@
         }
     }
 
-    JavaScriptIsolate(IJsSandboxIsolate jsIsolateStub, JavaScriptSandbox sandbox,
-            IsolateStartupParameters settings) {
+    JavaScriptIsolate(@NonNull IJsSandboxIsolate jsIsolateStub, @NonNull JavaScriptSandbox sandbox,
+            @NonNull IsolateStartupParameters settings) {
         mJsSandbox = sandbox;
         mJsIsolateStub = jsIsolateStub;
         mStartupParameters = settings;
@@ -266,6 +273,7 @@
     @SuppressWarnings("NullAway")
     @NonNull
     public ListenableFuture<String> evaluateJavaScriptAsync(@NonNull String code) {
+        Objects.requireNonNull(code);
         if (!mSandboxClosed.get() && mJsSandbox.isFeatureSupported(
                 JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT)) {
             // This process can be made more memory efficient by converting the String to
@@ -324,6 +332,7 @@
     @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT,
             enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
     public ListenableFuture<String> evaluateJavaScriptAsync(@NonNull byte[] code) {
+        Objects.requireNonNull(code);
         if (mJsIsolateStub == null) {
             throw new IllegalStateException(
                     "Calling evaluateJavaScriptAsync() after closing the Isolate");
@@ -441,12 +450,11 @@
     @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER,
             enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
     public boolean provideNamedData(@NonNull String name, @NonNull byte[] inputBytes) {
+        Objects.requireNonNull(name);
+        Objects.requireNonNull(inputBytes);
         if (mJsIsolateStub == null) {
             throw new IllegalStateException("Calling provideNamedData() after closing the Isolate");
         }
-        if (name == null) {
-            throw new NullPointerException("name parameter cannot be null");
-        }
         try {
             AssetFileDescriptor codeAfd = Utils.writeBytesIntoPipeAsync(inputBytes,
                     mJsSandbox.mThreadPoolTaskExecutor);
@@ -465,8 +473,8 @@
         return false;
     }
 
-    void handleEvaluationError(CallbackToFutureAdapter.Completer<String> completer,
-            int type, String error) {
+    void handleEvaluationError(@NonNull CallbackToFutureAdapter.Completer<String> completer,
+            int type, @NonNull String error) {
         boolean crashing = false;
         switch (type) {
             case IJsSandboxIsolateSyncCallback.JS_EVALUATION_ERROR:
@@ -488,8 +496,8 @@
         }
     }
 
-    void handleEvaluationResult(CallbackToFutureAdapter.Completer<String> completer,
-            String result) {
+    void handleEvaluationResult(@NonNull CallbackToFutureAdapter.Completer<String> completer,
+            @NonNull String result) {
         completer.set(result);
         removePending(completer);
     }
@@ -501,7 +509,7 @@
 
     // Cancel all pending and future evaluations with the given exception.
     // Only the first call to this method has any effect.
-    void cancelAllPendingEvaluations(Exception e) {
+    void cancelAllPendingEvaluations(@NonNull Exception e) {
         final HashSet<CallbackToFutureAdapter.Completer<String>> pendingSet;
         synchronized (mSetLock) {
             if (mPendingCompleterSet == null) return;
@@ -514,7 +522,7 @@
         }
     }
 
-    void removePending(CallbackToFutureAdapter.Completer<String> completer) {
+    void removePending(@NonNull CallbackToFutureAdapter.Completer<String> completer) {
         synchronized (mSetLock) {
             if (mPendingCompleterSet != null) {
                 mPendingCompleterSet.remove(completer);
@@ -568,12 +576,8 @@
             enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
     public void setConsoleCallback(@NonNull Executor executor,
             @NonNull JavaScriptConsoleCallback callback) {
-        if (executor == null) {
-            throw new IllegalArgumentException("executor cannot be null");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         if (mJsIsolateStub == null) {
             throw new IllegalStateException(
                     "Calling setConsoleCallback() after closing the Isolate");
@@ -597,6 +601,7 @@
     @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING,
             enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
     public void setConsoleCallback(@NonNull JavaScriptConsoleCallback callback) {
+        Objects.requireNonNull(callback);
         setConsoleCallback(mJsSandbox.getMainExecutor(), callback);
     }
 
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
index d98ab90..f7541ce 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
@@ -45,6 +45,7 @@
 import java.lang.annotation.Target;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -248,7 +249,7 @@
                     new RuntimeException("JavaScriptSandbox internal error: onNullBinding()"));
         }
 
-        private void runShutdownTasks(Exception e) {
+        private void runShutdownTasks(@NonNull Exception e) {
             if (mJsSandbox != null) {
                 mJsSandbox.close();
             } else {
@@ -261,7 +262,7 @@
             mCompleter = null;
         }
 
-        ConnectionSetup(Context context,
+        ConnectionSetup(@NonNull Context context,
                 @NonNull CallbackToFutureAdapter.Completer<JavaScriptSandbox> completer) {
             mContext = context;
             mCompleter = completer;
@@ -286,6 +287,7 @@
     @NonNull
     public static ListenableFuture<JavaScriptSandbox> createConnectedInstanceAsync(
             @NonNull Context context) {
+        Objects.requireNonNull(context);
         if (!isSupported()) {
             throw new SandboxUnsupportedException("The system does not support JavaScriptSandbox");
         }
@@ -313,6 +315,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     public static ListenableFuture<JavaScriptSandbox> createConnectedInstanceForTestingAsync(
             @NonNull Context context) {
+        Objects.requireNonNull(context);
         ComponentName compName = new ComponentName(context, JS_SANDBOX_SERVICE_NAME);
         int flag = Context.BIND_AUTO_CREATE;
         return bindToServiceWithCallback(context, compName, flag);
@@ -326,7 +329,6 @@
      *
      * @return true if JavaScriptSandbox is supported and false otherwise.
      */
-    @NonNull
     public static boolean isSupported() {
         PackageInfo systemWebViewPackage = WebView.getCurrentWebViewPackage();
         if (systemWebViewPackage == null) {
@@ -343,7 +345,7 @@
 
     @NonNull
     private static ListenableFuture<JavaScriptSandbox> bindToServiceWithCallback(
-            Context context, ComponentName compName, int flag) {
+            @NonNull Context context, @NonNull ComponentName compName, int flag) {
         Intent intent = new Intent();
         intent.setComponent(compName);
         return CallbackToFutureAdapter.getFuture(completer -> {
@@ -381,7 +383,8 @@
 
     // We prevent direct initializations of this class.
     // Use JavaScriptSandbox.createConnectedInstance().
-    JavaScriptSandbox(ConnectionSetup connectionSetup, IJsSandboxService jsSandboxService) {
+    JavaScriptSandbox(@NonNull ConnectionSetup connectionSetup,
+            @NonNull IJsSandboxService jsSandboxService) {
         mConnection = connectionSetup;
         synchronized (mLock) {
             mJsSandboxService = jsSandboxService;
@@ -407,6 +410,7 @@
      */
     @NonNull
     public JavaScriptIsolate createIsolate(@NonNull IsolateStartupParameters settings) {
+        Objects.requireNonNull(settings);
         synchronized (mLock) {
             if (mJsSandboxService == null) {
                 throw new IllegalStateException(
@@ -462,9 +466,10 @@
     }
 
     @GuardedBy("mLock")
+    @NonNull
     @SuppressWarnings("NullAway")
-    private JavaScriptIsolate createJsIsolateLocked(IJsSandboxIsolate isolateStub,
-            IsolateStartupParameters settings) {
+    private JavaScriptIsolate createJsIsolateLocked(@NonNull IJsSandboxIsolate isolateStub,
+            @NonNull IsolateStartupParameters settings) {
         JavaScriptIsolate isolate = new JavaScriptIsolate(isolateStub, this, settings);
         mActiveIsolateSet.add(isolate);
         return isolate;
@@ -483,7 +488,8 @@
      * @return {@code true} if supported, {@code false} otherwise
      */
     @SuppressWarnings("NullAway")
-    public boolean isFeatureSupported(@NonNull @JsSandboxFeature String feature) {
+    public boolean isFeatureSupported(@JsSandboxFeature @NonNull String feature) {
+        Objects.requireNonNull(feature);
         synchronized (mLock) {
             if (mJsSandboxService == null) {
                 throw new IllegalStateException(
@@ -496,7 +502,7 @@
         }
     }
 
-    void removeFromIsolateSet(JavaScriptIsolate isolate) {
+    void removeFromIsolateSet(@NonNull JavaScriptIsolate isolate) {
         synchronized (mLock) {
             if (mActiveIsolateSet != null) {
                 mActiveIsolateSet.remove(isolate);
@@ -562,6 +568,7 @@
         }
     }
 
+    @NonNull
     Executor getMainExecutor() {
         return ContextCompat.getMainExecutor(mConnection.mContext);
     }
diff --git a/libraryversions.toml b/libraryversions.toml
index ec838a5..c511569 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -14,12 +14,12 @@
 BLUETOOTH = "1.0.0-alpha01"
 BROWSER = "1.6.0-alpha01"
 BUILDSRC_TESTS = "1.0.0-alpha01"
-CAMERA = "1.3.0-alpha06"
+CAMERA = "1.3.0-alpha07"
 CAMERA_PIPE = "1.0.0-alpha01"
 CARDVIEW = "1.1.0-alpha01"
 CAR_APP = "1.4.0-alpha01"
 COLLECTION = "1.3.0-alpha05"
-COMPOSE = "1.5.0-alpha03"
+COMPOSE = "1.5.0-alpha04"
 COMPOSE_COMPILER = "1.4.6"
 COMPOSE_MATERIAL3 = "1.2.0-alpha01"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
@@ -29,7 +29,7 @@
 CONSTRAINTLAYOUT_CORE = "1.1.0-alpha10"
 CONTENTPAGER = "1.1.0-alpha01"
 COORDINATORLAYOUT = "1.3.0-alpha01"
-CORE = "1.11.0-alpha03"
+CORE = "1.11.0-alpha04"
 CORE_ANIMATION = "1.0.0-beta02"
 CORE_ANIMATION_TESTING = "1.0.0-beta01"
 CORE_APPDIGEST = "1.0.0-alpha01"
@@ -41,7 +41,7 @@
 CORE_ROLE = "1.2.0-alpha01"
 CORE_SPLASHSCREEN = "1.1.0-alpha01"
 CORE_UWB = "1.0.0-alpha06"
-CREDENTIALS = "1.0.0-alpha07"
+CREDENTIALS = "1.0.0-alpha08"
 CURSORADAPTER = "1.1.0-alpha01"
 CUSTOMVIEW = "1.2.0-alpha03"
 CUSTOMVIEW_POOLINGCONTAINER = "1.1.0-alpha01"
@@ -52,21 +52,23 @@
 DYNAMICANIMATION = "1.1.0-alpha04"
 DYNAMICANIMATION_KTX = "1.0.0-alpha04"
 EMOJI = "1.2.0-alpha03"
-EMOJI2 = "1.4.0-beta02"
+EMOJI2 = "1.4.0-beta03"
 ENTERPRISE = "1.1.0-rc01"
 EXIFINTERFACE = "1.4.0-alpha01"
-FRAGMENT = "1.6.0-beta01"
+FRAGMENT = "1.6.0-rc01"
 FUTURES = "1.2.0-alpha01"
-GLANCE = "1.0.0-alpha06"
-GLANCE_TEMPLATE = "1.0.0-alpha01"
+GLANCE = "1.0.0-beta01"
+GLANCE_PREVIEW = "1.0.0-alpha06"
+GLANCE_TEMPLATE = "1.0.0-alpha06"
+GLANCE_WEAR_TILES = "1.0.0-alpha06"
 GRAPHICS_CORE = "1.0.0-alpha04"
 GRAPHICS_FILTERS = "1.0.0-alpha01"
-GRAPHICS_SHAPES = "1.0.0-alpha02"
+GRAPHICS_SHAPES = "1.0.0-alpha03"
 GRIDLAYOUT = "1.1.0-alpha02"
 HEALTH_CONNECT = "1.0.0-alpha11"
 HEALTH_SERVICES_CLIENT = "1.0.0-beta04"
 HEIFWRITER = "1.1.0-alpha02"
-HILT = "1.1.0-alpha02"
+HILT = "1.1.0-alpha03"
 HILT_NAVIGATION_COMPOSE = "1.1.0-alpha02"
 INPUT_MOTIONPREDICTION = "1.0.0-beta02"
 INSPECTION = "1.0.0"
@@ -86,18 +88,18 @@
 MEDIA2 = "1.3.0-alpha01"
 MEDIAROUTER = "1.5.0-alpha01"
 METRICS = "1.0.0-alpha05"
-NAVIGATION = "2.6.0-beta01"
+NAVIGATION = "2.6.0-rc01"
 PAGING = "3.2.0-alpha05"
 PAGING_COMPOSE = "1.0.0-alpha19"
 PALETTE = "1.1.0-alpha01"
 PERCENTLAYOUT = "1.1.0-alpha01"
 PREFERENCE = "1.3.0-alpha01"
 PRINT = "1.1.0-beta01"
-PRIVACYSANDBOX_ADS = "1.0.0-beta03"
+PRIVACYSANDBOX_ADS = "1.0.0-beta04"
 PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha02"
 PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha04"
 PRIVACYSANDBOX_TOOLS = "1.0.0-alpha04"
-PRIVACYSANDBOX_UI = "1.0.0-alpha02"
+PRIVACYSANDBOX_UI = "1.0.0-alpha03"
 PROFILEINSTALLER = "1.4.0-alpha01"
 RECOMMENDATION = "1.1.0-alpha01"
 RECYCLERVIEW = "1.4.0-alpha01"
@@ -106,7 +108,7 @@
 RESOURCEINSPECTION = "1.1.0-alpha01"
 ROOM = "2.6.0-alpha02"
 SAVEDSTATE = "1.3.0-alpha01"
-SECURITY = "1.1.0-alpha06"
+SECURITY = "1.1.0-alpha07"
 SECURITY_APP_AUTHENTICATOR = "1.0.0-alpha03"
 SECURITY_APP_AUTHENTICATOR_TESTING = "1.0.0-alpha02"
 SECURITY_BIOMETRIC = "1.0.0-alpha01"
@@ -124,7 +126,7 @@
 SWIPEREFRESHLAYOUT = "1.2.0-alpha01"
 TESTEXT = "1.0.0-alpha02"
 TESTSCREENSHOT = "1.0.0-alpha01"
-TEST_UIAUTOMATOR = "2.3.0-alpha03"
+TEST_UIAUTOMATOR = "2.3.0-alpha04"
 TEXT = "1.0.0-alpha01"
 TRACING = "1.2.0-beta04"
 TRACING_PERFETTO = "1.0.0-alpha15"
@@ -137,17 +139,17 @@
 VERSIONED_PARCELABLE = "1.2.0-alpha01"
 VIEWPAGER = "1.1.0-alpha02"
 VIEWPAGER2 = "1.2.0-alpha01"
-WEAR = "1.3.0-alpha05"
-WEAR_COMPOSE = "1.2.0-alpha09"
-WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha03"
+WEAR = "1.3.0-alpha06"
+WEAR_COMPOSE = "1.2.0-alpha10"
+WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha04"
 WEAR_INPUT = "1.2.0-alpha03"
 WEAR_INPUT_TESTING = "1.2.0-alpha03"
 WEAR_ONGOING = "1.1.0-alpha01"
 WEAR_PHONE_INTERACTIONS = "1.1.0-alpha04"
-WEAR_PROTOLAYOUT = "1.0.0-alpha08"
+WEAR_PROTOLAYOUT = "1.0.0-alpha09"
 WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha01"
-WEAR_TILES = "1.2.0-alpha04"
-WEAR_WATCHFACE = "1.2.0-alpha08"
+WEAR_TILES = "1.2.0-alpha05"
+WEAR_WATCHFACE = "1.2.0-alpha09"
 WEBKIT = "1.8.0-alpha01"
 WINDOW = "1.2.0-alpha01"
 WINDOW_EXTENSIONS = "1.2.0-alpha01"
@@ -204,7 +206,6 @@
 EXIFINTERFACE = { group = "androidx.exifinterface", atomicGroupVersion = "versions.EXIFINTERFACE" }
 FRAGMENT = { group = "androidx.fragment", atomicGroupVersion = "versions.FRAGMENT" }
 GLANCE = { group = "androidx.glance", atomicGroupVersion = "versions.GLANCE" }
-GLANCE_TEMPLATE = { group = "androidx.template", atomicGroupVersion = "versions.GLANCE_TEMPLATE" }
 GRAPHICS = { group = "androidx.graphics" }
 GRAPHICS_FILTERS = { group = "androidx.graphics.filters", atomicGroupVersion = "versions.GRAPHICS_FILTERS" }
 GRIDLAYOUT = { group = "androidx.gridlayout", atomicGroupVersion = "versions.GRIDLAYOUT" }
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt b/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
index 6482e0a..a7a8099 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
@@ -34,6 +34,7 @@
 import com.intellij.psi.impl.source.PsiImmediateClassType
 import org.jetbrains.kotlin.asJava.elements.KtLightTypeParameter
 import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
 import org.jetbrains.kotlin.psi.KtNullableType
 import org.jetbrains.kotlin.psi.KtTypeReference
 import org.jetbrains.uast.UAnnotated
@@ -46,6 +47,7 @@
 import org.jetbrains.uast.kotlin.KotlinUField
 import org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression
 import org.jetbrains.uast.resolveToUElement
+import org.jetbrains.uast.toUElement
 
 /**
  * Lint check for ensuring that [androidx.lifecycle.MutableLiveData] values are never null when
@@ -211,7 +213,7 @@
                     "Cannot set non-nullable LiveData value to `null`",
                     fixes
                 )
-            } else if (argument.isNullable()) {
+            } else if (argument.isNullable(context)) {
                 fixes.add(
                     fix().name("Add non-null asserted (!!) call")
                         .replace().with("!!").range(context.getLocation(argument)).end().build()
@@ -258,10 +260,17 @@
  *
  * @return `true` if instance is nullable, `false` otherwise.
  */
-internal fun UElement.isNullable(): Boolean {
+internal fun UElement.isNullable(context: JavaContext): Boolean {
     if (this is UCallExpression) {
         val psiMethod = resolve() ?: return false
-        return psiMethod.hasAnnotation(NULLABLE_ANNOTATION)
+        val sourceMethod = psiMethod.toUElement()?.sourcePsi
+        if (sourceMethod is KtCallableDeclaration) {
+            // if we have source, check the suspend return type
+            return sourceMethod.typeReference?.typeElement is KtNullableType
+        }
+        // Suspend functions have @Nullable Object return type in JVM
+        val isSuspendMethod = !context.evaluator.isSuspend(psiMethod)
+        return psiMethod.hasAnnotation(NULLABLE_ANNOTATION) && isSuspendMethod
     } else if (this is UReferenceExpression) {
         return (resolveToUElement() as? UAnnotated)?.findAnnotation(NULLABLE_ANNOTATION) != null
     }
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/livedata/core/lint/NonNullableMutableLiveDataDetectorTest.kt b/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/livedata/core/lint/NonNullableMutableLiveDataDetectorTest.kt
index b6c0412..97e2074 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/livedata/core/lint/NonNullableMutableLiveDataDetectorTest.kt
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/livedata/core/lint/NonNullableMutableLiveDataDetectorTest.kt
@@ -23,6 +23,7 @@
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
 import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Ignore
@@ -38,7 +39,7 @@
         mutableListOf(NonNullableMutableLiveDataDetector.ISSUE)
 
     private fun check(vararg files: TestFile): TestLintResult {
-        return lint().files(*files, *STUBS)
+        return lint().files(*files, *STUBS).testModes(TestMode.DEFAULT)
             .run()
     }
 
@@ -101,7 +102,6 @@
         ).expectClean()
     }
 
-    @Ignore("b/196832482")
     @Test
     fun helperMethodFails() {
         check(
@@ -132,7 +132,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun variableAssignmentFails() {
         check(
@@ -170,7 +169,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralFailField() {
         check(
@@ -197,7 +195,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralFailMultipleFields() {
         check(
@@ -255,7 +252,6 @@
         ).expectClean()
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralFailMultipleAssignment() {
         check(
@@ -283,7 +279,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralFailFieldAndIgnore() {
         check(
@@ -356,7 +351,6 @@
         ).expectClean()
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralFailFieldAndLocalVariable() {
         check(
@@ -388,7 +382,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nullLiteralQuickFix() {
         check(
@@ -414,7 +407,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun classHierarchyTest() {
         check(
@@ -457,7 +449,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun differentClassSameFieldTestFirstNull() {
         check(
@@ -508,7 +499,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun differentClassSameFieldTestSecondNull() {
         check(
@@ -559,7 +549,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun nestedClassSameFieldTest() {
         check(
@@ -603,7 +592,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun modifiersFieldTest() {
         check(
@@ -644,7 +632,6 @@
         )
     }
 
-    @Ignore("b/196832482")
     @Test
     fun implementationClassTest() {
         check(
@@ -820,4 +807,49 @@
             ).indented()
         ).expectClean()
     }
+
+    @Test
+    fun suspendFunction() {
+        check(kotlin("""
+            package com.example
+
+            import androidx.lifecycle.MutableLiveData
+
+            class Foo(
+                var target: MutableLiveData<Boolean>
+            ) {
+
+                suspend fun foo() {
+                    target.value = nonNullable()
+                }
+
+                suspend fun nonNullable() = true
+            }
+        """).indented()).expectClean()
+    }
+
+    @Test
+    fun nullableSuspendFunction() {
+        check(kotlin("""
+            package com.example
+
+            import androidx.lifecycle.MutableLiveData
+
+            class Foo(
+                var target: MutableLiveData<String>
+            ) {
+
+                suspend fun foo() {
+                    target.value = nullable()
+                }
+
+                suspend fun nullable(): String? = null
+            }
+        """).indented()).expect("""
+src/com/example/Foo.kt:10: Error: Expected non-nullable value [NullSafeMutableLiveData]
+        target.value = nullable()
+                       ~~~~~~~~~~
+1 errors, 0 warnings
+        """)
+    }
 }
diff --git a/lifecycle/lifecycle-runtime/proguard-rules.pro b/lifecycle/lifecycle-runtime/proguard-rules.pro
index e4b2c95..95192c1 100644
--- a/lifecycle/lifecycle-runtime/proguard-rules.pro
+++ b/lifecycle/lifecycle-runtime/proguard-rules.pro
@@ -7,9 +7,6 @@
     <fields>;
 }
 
--keep !interface * implements androidx.lifecycle.LifecycleObserver {
-}
-
 -keep class * implements androidx.lifecycle.GeneratedAdapter {
     <init>(...);
 }
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java
new file mode 100644
index 0000000..99fc876
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import static androidx.annotation.RestrictTo.Scope.TESTS;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+/** @noinspection unused*/
+public class RestrictToTestsAnnotationUsageJava {
+    @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
+    public void testMethodFullyQualified() {}
+
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    public void testMethodOuterClass() {}
+
+    @RestrictTo(Scope.TESTS)
+    public void testMethodInnerClass() {}
+
+    @RestrictTo(TESTS)
+    public void testMethodStaticImport() {}
+
+    @RestrictTo({Scope.TESTS, Scope.LIBRARY})
+    public void testMethodVarArg() {}
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
new file mode 100644
index 0000000..2d1622d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("unused")
+
+package androidx
+
+import androidx.annotation.RestrictTo
+
+class RestrictToTestsAnnotationUsageKotlin {
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    fun testMethod() {}
+
+    @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
+    fun testMethodVarArg() {}
+
+    @get:RestrictTo(RestrictTo.Scope.TESTS)
+    val testPropertyGet = "test"
+}
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 9bf1025..7466c91 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -70,6 +70,7 @@
                 NullabilityAnnotationsDetector.ISSUE,
                 IgnoreClassLevelDetector.ISSUE,
                 ExperimentalPropertyAnnotationDetector.ISSUE,
+                BanRestrictToTestsScope.ISSUE,
                 // Temporarily disable AIDL lint check due to b/278871118.
                 // UnstableAidlAnnotationDetector.ISSUE,
                 // MissingJvmDefaultWithCompatibilityDetector is intentionally left out of the
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
new file mode 100644
index 0000000..487fd61
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.getFqName
+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
+import com.android.tools.lint.detector.api.Incident
+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 org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UReferenceExpression
+import org.jetbrains.uast.util.isArrayInitializer
+
+class BanRestrictToTestsScope : Detector(), Detector.UastScanner {
+
+    override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return AnnotationChecker(context)
+    }
+
+    private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
+        override fun visitAnnotation(node: UAnnotation) {
+            if (node.qualifiedName != "androidx.annotation.RestrictTo") return
+
+            // Resolve the FQN for all arguments to value parameter.
+            val scopes = node.findAttributeValue("value")?.let { value ->
+                if (value.isArrayInitializer()) {
+                    (value as? UCallExpression)?.valueArguments?.mapNotNull { arg ->
+                        arg as? UReferenceExpression
+                    } ?: emptyList()
+                } else if (value is UReferenceExpression) {
+                    listOfNotNull(value)
+                } else {
+                    emptyList()
+                }
+            }?.mapNotNull { expr ->
+                expr.resolve()?.getFqName()
+            } ?: emptyList()
+
+            if (!scopes.contains("androidx.annotation.RestrictTo.Scope.TESTS")) return
+
+            val incident = Incident(context)
+                .issue(ISSUE)
+                .location(context.getNameLocation(node))
+                .message("Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`")
+                .scope(node)
+
+            // If there's only one scope, suggest replacement.
+            if (scopes.size == 1) {
+                // Extract Kotlin use-site target, if available.
+                val useSiteTarget = (node.sourcePsi as? KtAnnotationEntry)
+                    ?.useSiteTarget
+                    ?.getAnnotationUseSiteTarget()
+                    ?.renderName
+                    ?.let { "$it:" } ?: ""
+
+                val fix = fix().name("Replace with `@${useSiteTarget}VisibleForTesting`")
+                    .replace()
+                    .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
+                    .shortenNames()
+                    .build()
+                incident.fix(fix)
+            }
+
+            context.report(incident)
+        }
+    }
+
+    companion object {
+        val ISSUE = Issue.create(
+            "UsesRestrictToTestsScope",
+            "Uses @RestrictTo(TESTS) restriction scope",
+            "Use of @RestrictTo(TESTS) restriction scope is not allowed, use " +
+                "@VisibleForTesting instead.",
+            Category.CORRECTNESS, 5, Severity.ERROR,
+            Implementation(BanRestrictToTestsScope::class.java, Scope.JAVA_FILE_SCOPE)
+        )
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
new file mode 100644
index 0000000..08720a0
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanRestrictToTestsScopeTest : AbstractLintDetectorTest(
+    useDetector = BanRestrictToTestsScope(),
+    useIssues = listOf(BanRestrictToTestsScope.ISSUE),
+    stubs = arrayOf(Stubs.RestrictTo),
+) {
+
+    @Test
+    fun `Detection of @RestrictTo(TESTS) usage in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.RestrictToTestsAnnotationUsageJava"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/RestrictToTestsAnnotationUsageJava.java:26: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:29: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:32: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(Scope.TESTS)
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:35: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(TESTS)
+    ~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:38: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo({Scope.TESTS, Scope.LIBRARY})
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+5 errors, 0 warnings
+        """.trimIndent()
+
+        val fixDiffs = """
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 26: Replace with `@VisibleForTesting`:
+@@ -26 +26
+-     @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
++     @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 29: Replace with `@VisibleForTesting`:
+@@ -29 +29
+-     @RestrictTo(RestrictTo.Scope.TESTS)
++     @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 32: Replace with `@VisibleForTesting`:
+@@ -32 +32
+-     @RestrictTo(Scope.TESTS)
++     @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 35: Replace with `@VisibleForTesting`:
+@@ -35 +35
+-     @RestrictTo(TESTS)
++     @androidx.annotation.VisibleForTesting
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input)
+            .expect(expected)
+            .expectFixDiffs(fixDiffs)
+    }
+    @Test
+    fun `Detection of @RestrictTo(TESTS) usage in Kotlin sources`() {
+        val input = arrayOf(
+            ktSample("androidx.RestrictToTestsAnnotationUsageKotlin"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:24: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:27: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:30: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+    @get:RestrictTo(RestrictTo.Scope.TESTS)
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3 errors, 0 warnings
+        """.trimIndent()
+
+        val fixDiffs = """
+Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 24: Replace with `@VisibleForTesting`:
+@@ -24 +24
+-     @RestrictTo(RestrictTo.Scope.TESTS)
++     @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 30: Replace with `@get:VisibleForTesting`:
+@@ -30 +30
+-     @get:RestrictTo(RestrictTo.Scope.TESTS)
++     @get:androidx.annotation.VisibleForTesting
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input)
+            .expect(expected)
+            .expectFixDiffs(fixDiffs)
+    }
+}
diff --git a/media/OWNERS b/media/OWNERS
index 02db238..5e87314 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -4,6 +4,7 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 0787b8d..91a6177 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -19,7 +19,6 @@
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
-    id("androidx.stableaidl")
 }
 
 dependencies {
@@ -47,10 +46,6 @@
 
     buildTypes.all {
         consumerProguardFiles "proguard-rules.pro"
-
-        stableAidl {
-            version 1
-        }
     }
     namespace "androidx.media"
 }
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/MediaDescriptionCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/MediaDescriptionCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/MediaDescriptionCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/MediaDescriptionCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/MediaMetadataCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/MediaMetadataCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/MediaMetadataCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/MediaMetadataCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/RatingCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/RatingCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/RatingCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/RatingCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/IMediaControllerCallback.aidl b/media/media/src/main/aidl/android/support/v4/media/session/IMediaControllerCallback.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/IMediaControllerCallback.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/IMediaControllerCallback.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/IMediaSession.aidl b/media/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/IMediaSession.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/MediaSessionCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/MediaSessionCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl b/media/media/src/main/aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/PlaybackStateCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/session/PlaybackStateCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/PlaybackStateCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/PlaybackStateCompat.aidl
diff --git a/media/media/src/main/stableAidlImports/android/content/Intent.aidl b/media/media/src/main/stableAidlImports/android/content/Intent.aidl
deleted file mode 100644
index 0c8c241..0000000
--- a/media/media/src/main/stableAidlImports/android/content/Intent.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable Intent;
diff --git a/media/media/src/main/stableAidlImports/android/os/Bundle.aidl b/media/media/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/media/media/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl b/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
index a095df7..f4e5890 100644
--- a/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
+++ b/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
@@ -1,3 +1,18 @@
+/**
+ * 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.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl b/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl
deleted file mode 100644
index 5ec5a66..0000000
--- a/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.net;
-
-@JavaOnlyStableParcelable parcelable Uri;
diff --git a/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl b/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/media2/media2-widget/src/main/res/values-am/strings.xml b/media2/media2-widget/src/main/res/values-am/strings.xml
index c25eb1d..e95ccd2 100644
--- a/media2/media2-widget/src/main/res/values-am/strings.xml
+++ b/media2/media2-widget/src/main/res/values-am/strings.xml
@@ -45,5 +45,5 @@
     <string name="mcv2_next_button_desc" msgid="1204572886248099893">"ቀጣይ ማህደረ መረጃ"</string>
     <string name="mcv2_rewind_button_desc" msgid="578809901971186362">"በ10 ሰከንዶች አጠንጥን"</string>
     <string name="mcv2_ffwd_button_desc" msgid="611689280746097673">"ወደፊት በ30 ሰከንዶች ሂድ"</string>
-    <string name="mcv2_full_screen_button_desc" msgid="1609817079594941003">"ሙሉ ማያ ገጽ"</string>
+    <string name="mcv2_full_screen_button_desc" msgid="1609817079594941003">"ሙሉ ማያ ገፅ"</string>
 </resources>
diff --git a/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml b/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
index 7145c45..05ec6bb 100644
--- a/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
+++ b/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
@@ -29,7 +29,7 @@
     <string name="mcv2_non_music_title_unknown_text" msgid="2032814146738922144">"影片標題不明"</string>
     <string name="mcv2_music_title_unknown_text" msgid="6037645626002038645">"歌名不明"</string>
     <string name="mcv2_music_artist_unknown_text" msgid="5393558204040775454">"歌手不明"</string>
-    <string name="mcv2_playback_error_text" msgid="6061787693725630293">"無法播放您要求的影片"</string>
+    <string name="mcv2_playback_error_text" msgid="6061787693725630293">"無法播放你要求的影片"</string>
     <string name="mcv2_error_dialog_button" msgid="5940167897992933850">"好"</string>
     <string name="mcv2_back_button_desc" msgid="1540894858499118373">"返回"</string>
     <string name="mcv2_overflow_left_button_desc" msgid="2749567167276435888">"返回上一個按鈕清單"</string>
diff --git a/navigation/navigation-common-ktx/api/2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common/api/2.6.0-beta02.txt b/navigation/navigation-common/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+    ctor public ActionOnlyNavDirections(int actionId);
+    method public int component1();
+    method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+    method public int getActionId();
+    method public android.os.Bundle getArguments();
+    property public int actionId;
+    property public android.os.Bundle arguments;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+    ctor public AnimBuilder();
+    method public int getEnter();
+    method public int getExit();
+    method public int getPopEnter();
+    method public int getPopExit();
+    method public void setEnter(int);
+    method public void setExit(int);
+    method public void setPopEnter(int);
+    method public void setPopExit(int);
+    property public final int enter;
+    property public final int exit;
+    property public final int popEnter;
+    property public final int popExit;
+  }
+
+  public interface FloatingWindow {
+  }
+
+  public final class NamedNavArgument {
+    method public operator String component1();
+    method public operator androidx.navigation.NavArgument component2();
+    method public androidx.navigation.NavArgument getArgument();
+    method public String getName();
+    property public final androidx.navigation.NavArgument argument;
+    property public final String name;
+  }
+
+  public final class NamedNavArgumentKt {
+    method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavAction {
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+    ctor public NavAction(@IdRes int destinationId);
+    method public android.os.Bundle? getDefaultArguments();
+    method public int getDestinationId();
+    method public androidx.navigation.NavOptions? getNavOptions();
+    method public void setDefaultArguments(android.os.Bundle?);
+    method public void setNavOptions(androidx.navigation.NavOptions?);
+    property public final android.os.Bundle? defaultArguments;
+    property public final int destinationId;
+    property public final androidx.navigation.NavOptions? navOptions;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+    ctor public NavActionBuilder();
+    method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+    method public int getDestinationId();
+    method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+    method public void setDestinationId(int);
+    property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+    property public final int destinationId;
+  }
+
+  public interface NavArgs {
+  }
+
+  public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+    ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+    method public Args getValue();
+    method public boolean isInitialized();
+    property public Args value;
+  }
+
+  public final class NavArgument {
+    method public Object? getDefaultValue();
+    method public androidx.navigation.NavType<java.lang.Object> getType();
+    method public boolean isDefaultValuePresent();
+    method public boolean isNullable();
+    property public final Object? defaultValue;
+    property public final boolean isDefaultValuePresent;
+    property public final boolean isNullable;
+    property public final androidx.navigation.NavType<java.lang.Object> type;
+  }
+
+  public static final class NavArgument.Builder {
+    ctor public NavArgument.Builder();
+    method public androidx.navigation.NavArgument build();
+    method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+    method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+    method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+    ctor public NavArgumentBuilder();
+    method public androidx.navigation.NavArgument build();
+    method public Object? getDefaultValue();
+    method public boolean getNullable();
+    method public androidx.navigation.NavType<?> getType();
+    method public void setDefaultValue(Object?);
+    method public void setNullable(boolean);
+    method public void setType(androidx.navigation.NavType<?>);
+    property public final Object? defaultValue;
+    property public final boolean nullable;
+    property public final androidx.navigation.NavType<?> type;
+  }
+
+  public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    method public android.os.Bundle? getArguments();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.navigation.NavDestination getDestination();
+    method public String getId();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    property public final android.os.Bundle? arguments;
+    property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public final androidx.navigation.NavDestination destination;
+    property public final String id;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+  }
+
+  public final class NavDeepLink {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public static final class NavDeepLink.Builder {
+    method public androidx.navigation.NavDeepLink build();
+    method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+    method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+  }
+
+  @kotlin.DslMarker public @interface NavDeepLinkDsl {
+  }
+
+  @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+    ctor public NavDeepLinkDslBuilder();
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    method public void setAction(String?);
+    method public void setMimeType(String?);
+    method public void setUriPattern(String?);
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public final class NavDeepLinkDslBuilderKt {
+    method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+  }
+
+  public class NavDeepLinkRequest {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public android.net.Uri? getUri();
+    property public String? action;
+    property public String? mimeType;
+    property public android.net.Uri? uri;
+  }
+
+  public static final class NavDeepLinkRequest.Builder {
+    method public androidx.navigation.NavDeepLinkRequest build();
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+    field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+  }
+
+  public static final class NavDeepLinkRequest.Builder.Companion {
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+  }
+
+  public class NavDestination {
+    ctor public NavDestination(String navigatorName);
+    ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+    method public final void addDeepLink(String uriPattern);
+    method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+    method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+    method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+    method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method @IdRes public final int getId();
+    method public final CharSequence? getLabel();
+    method public final String getNavigatorName();
+    method public final androidx.navigation.NavGraph? getParent();
+    method public final String? getRoute();
+    method public boolean hasDeepLink(android.net.Uri deepLink);
+    method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+    method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+    method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+    method public final void putAction(@IdRes int actionId, @IdRes int destId);
+    method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+    method public final void removeAction(@IdRes int actionId);
+    method public final void removeArgument(String argumentName);
+    method public final void setId(@IdRes int);
+    method public final void setLabel(CharSequence?);
+    method public final void setRoute(String?);
+    property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+    property @IdRes public final int id;
+    property public final CharSequence? label;
+    property public final String navigatorName;
+    property public final androidx.navigation.NavGraph? parent;
+    property public final String? route;
+    field public static final androidx.navigation.NavDestination.Companion Companion;
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+    method public abstract kotlin.reflect.KClass<?> value();
+    property public abstract kotlin.reflect.KClass<?> value;
+  }
+
+  public static final class NavDestination.Companion {
+    method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+    method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+    method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+    method public D build();
+    method public final void deepLink(String uriPattern);
+    method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public final int getId();
+    method public final CharSequence? getLabel();
+    method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+    method public final String? getRoute();
+    method public final void setLabel(CharSequence?);
+    property public final int id;
+    property public final CharSequence? label;
+    property protected final androidx.navigation.Navigator<? extends D> navigator;
+    property public final String? route;
+  }
+
+  @kotlin.DslMarker public @interface NavDestinationDsl {
+  }
+
+  public interface NavDirections {
+    method @IdRes public int getActionId();
+    method public android.os.Bundle getArguments();
+    property @IdRes public abstract int actionId;
+    property public abstract android.os.Bundle arguments;
+  }
+
+  public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+    method public final void addAll(androidx.navigation.NavGraph other);
+    method public final void addDestination(androidx.navigation.NavDestination node);
+    method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+    method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+    method public final void clear();
+    method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+    method public final androidx.navigation.NavDestination? findNode(String? route);
+    method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+    method @Deprecated @IdRes public final int getStartDestination();
+    method @IdRes public final int getStartDestinationId();
+    method public final String? getStartDestinationRoute();
+    method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+    method public final void remove(androidx.navigation.NavDestination node);
+    method public final void setStartDestination(int startDestId);
+    method public final void setStartDestination(String startDestRoute);
+    property @IdRes public final int startDestinationId;
+    property public final String? startDestinationRoute;
+    field public static final androidx.navigation.NavGraph.Companion Companion;
+  }
+
+  public static final class NavGraph.Companion {
+    method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+    method public final void addDestination(androidx.navigation.NavDestination destination);
+    method public androidx.navigation.NavGraph build();
+    method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+    method public final androidx.navigation.NavigatorProvider getProvider();
+    method public final operator void unaryPlus(androidx.navigation.NavDestination);
+    property public final androidx.navigation.NavigatorProvider provider;
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavGraphKt {
+    method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+    method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+    method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+    ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph createDestination();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  public final class NavOptions {
+    method @AnimRes @AnimatorRes public int getEnterAnim();
+    method @AnimRes @AnimatorRes public int getExitAnim();
+    method @AnimRes @AnimatorRes public int getPopEnterAnim();
+    method @AnimRes @AnimatorRes public int getPopExitAnim();
+    method @Deprecated @IdRes public int getPopUpTo();
+    method @IdRes public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean isPopUpToInclusive();
+    method public boolean shouldLaunchSingleTop();
+    method public boolean shouldPopUpToSaveState();
+    method public boolean shouldRestoreState();
+    property @AnimRes @AnimatorRes public final int enterAnim;
+    property @AnimRes @AnimatorRes public final int exitAnim;
+    property @AnimRes @AnimatorRes public final int popEnterAnim;
+    property @AnimRes @AnimatorRes public final int popExitAnim;
+    property @IdRes public final int popUpToId;
+    property public final String? popUpToRoute;
+  }
+
+  public static final class NavOptions.Builder {
+    ctor public NavOptions.Builder();
+    method public androidx.navigation.NavOptions build();
+    method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+    method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+    method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+    method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+    method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+    ctor public NavOptionsBuilder();
+    method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+    method public boolean getLaunchSingleTop();
+    method @Deprecated public int getPopUpTo();
+    method public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean getRestoreState();
+    method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void setLaunchSingleTop(boolean);
+    method @Deprecated public void setPopUpTo(int);
+    method public void setRestoreState(boolean);
+    property public final boolean launchSingleTop;
+    property @Deprecated public final int popUpTo;
+    property public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final boolean restoreState;
+  }
+
+  public final class NavOptionsBuilderKt {
+    method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+  }
+
+  @kotlin.DslMarker public @interface NavOptionsDsl {
+  }
+
+  public abstract class NavType<T> {
+    ctor public NavType(boolean isNullableAllowed);
+    method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+    method public abstract operator T? get(android.os.Bundle bundle, String key);
+    method public String getName();
+    method public boolean isNullableAllowed();
+    method public abstract T parseValue(String value);
+    method public T parseValue(String value, T previousValue);
+    method public abstract void put(android.os.Bundle bundle, String key, T value);
+    method public String serializeAsValue(T value);
+    property public boolean isNullableAllowed;
+    property public String name;
+    field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+    field public static final androidx.navigation.NavType.Companion Companion;
+    field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+    field public static final androidx.navigation.NavType<int[]> IntArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+    field public static final androidx.navigation.NavType<long[]> LongArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+    field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+    field public static final androidx.navigation.NavType<java.lang.String> StringType;
+  }
+
+  public static final class NavType.Companion {
+    method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+  }
+
+  public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+    ctor public NavType.EnumType(Class<D> type);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.ParcelableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+    ctor public NavType.ParcelableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.SerializableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+    ctor public NavType.SerializableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+    ctor public Navigator();
+    method public abstract D createDestination();
+    method protected final androidx.navigation.NavigatorState getState();
+    method public final boolean isAttached();
+    method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+    method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void onRestoreState(android.os.Bundle savedState);
+    method public android.os.Bundle? onSaveState();
+    method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+    method public boolean popBackStack();
+    property public final boolean isAttached;
+    property protected final androidx.navigation.NavigatorState state;
+  }
+
+  public static interface Navigator.Extras {
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+    method public abstract String value();
+    property public abstract String value;
+  }
+
+  public class NavigatorProvider {
+    ctor public NavigatorProvider();
+    method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+    method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+  }
+
+  public final class NavigatorProviderKt {
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+    method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+  }
+
+  public abstract class NavigatorState {
+    ctor public NavigatorState();
+    method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+    ctor public PopUpToBuilder();
+    method public boolean getInclusive();
+    method public boolean getSaveState();
+    method public void setInclusive(boolean);
+    method public void setSaveState(boolean);
+    property public final boolean inclusive;
+    property public final boolean saveState;
+  }
+
+}
+
diff --git a/navigation/navigation-common/api/current.ignore b/navigation/navigation-common/api/current.ignore
deleted file mode 100644
index 8eebd5d..0000000
--- a/navigation/navigation-common/api/current.ignore
+++ /dev/null
@@ -1,9 +0,0 @@
-// Baseline format: 1.0
-InvalidNullConversion: androidx.navigation.NavType#put(android.os.Bundle, String, T) parameter #2:
-    Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.put(android.os.Bundle bundle, String key, T value)
-InvalidNullConversion: androidx.navigation.NavType.ParcelableType#put(android.os.Bundle, String, D) parameter #2:
-    Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.ParcelableType.put(android.os.Bundle bundle, String key, D value)
-
-
-RemovedClass: androidx.navigation.NavArgsLazyKt:
-    Removed class androidx.navigation.NavArgsLazyKt
diff --git a/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+    ctor public ActionOnlyNavDirections(int actionId);
+    method public int component1();
+    method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+    method public int getActionId();
+    method public android.os.Bundle getArguments();
+    property public int actionId;
+    property public android.os.Bundle arguments;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+    ctor public AnimBuilder();
+    method public int getEnter();
+    method public int getExit();
+    method public int getPopEnter();
+    method public int getPopExit();
+    method public void setEnter(int);
+    method public void setExit(int);
+    method public void setPopEnter(int);
+    method public void setPopExit(int);
+    property public final int enter;
+    property public final int exit;
+    property public final int popEnter;
+    property public final int popExit;
+  }
+
+  public interface FloatingWindow {
+  }
+
+  public final class NamedNavArgument {
+    method public operator String component1();
+    method public operator androidx.navigation.NavArgument component2();
+    method public androidx.navigation.NavArgument getArgument();
+    method public String getName();
+    property public final androidx.navigation.NavArgument argument;
+    property public final String name;
+  }
+
+  public final class NamedNavArgumentKt {
+    method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavAction {
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+    ctor public NavAction(@IdRes int destinationId);
+    method public android.os.Bundle? getDefaultArguments();
+    method public int getDestinationId();
+    method public androidx.navigation.NavOptions? getNavOptions();
+    method public void setDefaultArguments(android.os.Bundle?);
+    method public void setNavOptions(androidx.navigation.NavOptions?);
+    property public final android.os.Bundle? defaultArguments;
+    property public final int destinationId;
+    property public final androidx.navigation.NavOptions? navOptions;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+    ctor public NavActionBuilder();
+    method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+    method public int getDestinationId();
+    method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+    method public void setDestinationId(int);
+    property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+    property public final int destinationId;
+  }
+
+  public interface NavArgs {
+  }
+
+  public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+    ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+    method public Args getValue();
+    method public boolean isInitialized();
+    property public Args value;
+  }
+
+  public final class NavArgument {
+    method public Object? getDefaultValue();
+    method public androidx.navigation.NavType<java.lang.Object> getType();
+    method public boolean isDefaultValuePresent();
+    method public boolean isNullable();
+    property public final Object? defaultValue;
+    property public final boolean isDefaultValuePresent;
+    property public final boolean isNullable;
+    property public final androidx.navigation.NavType<java.lang.Object> type;
+  }
+
+  public static final class NavArgument.Builder {
+    ctor public NavArgument.Builder();
+    method public androidx.navigation.NavArgument build();
+    method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+    method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+    method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+    ctor public NavArgumentBuilder();
+    method public androidx.navigation.NavArgument build();
+    method public Object? getDefaultValue();
+    method public boolean getNullable();
+    method public androidx.navigation.NavType<?> getType();
+    method public void setDefaultValue(Object?);
+    method public void setNullable(boolean);
+    method public void setType(androidx.navigation.NavType<?>);
+    property public final Object? defaultValue;
+    property public final boolean nullable;
+    property public final androidx.navigation.NavType<?> type;
+  }
+
+  public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    method public android.os.Bundle? getArguments();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.navigation.NavDestination getDestination();
+    method public String getId();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    property public final android.os.Bundle? arguments;
+    property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public final androidx.navigation.NavDestination destination;
+    property public final String id;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+  }
+
+  public final class NavDeepLink {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public static final class NavDeepLink.Builder {
+    method public androidx.navigation.NavDeepLink build();
+    method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+    method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+  }
+
+  @kotlin.DslMarker public @interface NavDeepLinkDsl {
+  }
+
+  @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+    ctor public NavDeepLinkDslBuilder();
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    method public void setAction(String?);
+    method public void setMimeType(String?);
+    method public void setUriPattern(String?);
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public final class NavDeepLinkDslBuilderKt {
+    method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+  }
+
+  public class NavDeepLinkRequest {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public android.net.Uri? getUri();
+    property public String? action;
+    property public String? mimeType;
+    property public android.net.Uri? uri;
+  }
+
+  public static final class NavDeepLinkRequest.Builder {
+    method public androidx.navigation.NavDeepLinkRequest build();
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+    field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+  }
+
+  public static final class NavDeepLinkRequest.Builder.Companion {
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+  }
+
+  public class NavDestination {
+    ctor public NavDestination(String navigatorName);
+    ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+    method public final void addDeepLink(String uriPattern);
+    method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+    method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+    method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+    method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method @IdRes public final int getId();
+    method public final CharSequence? getLabel();
+    method public final String getNavigatorName();
+    method public final androidx.navigation.NavGraph? getParent();
+    method public final String? getRoute();
+    method public boolean hasDeepLink(android.net.Uri deepLink);
+    method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+    method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+    method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+    method public final void putAction(@IdRes int actionId, @IdRes int destId);
+    method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+    method public final void removeAction(@IdRes int actionId);
+    method public final void removeArgument(String argumentName);
+    method public final void setId(@IdRes int);
+    method public final void setLabel(CharSequence?);
+    method public final void setRoute(String?);
+    property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+    property @IdRes public final int id;
+    property public final CharSequence? label;
+    property public final String navigatorName;
+    property public final androidx.navigation.NavGraph? parent;
+    property public final String? route;
+    field public static final androidx.navigation.NavDestination.Companion Companion;
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+    method public abstract kotlin.reflect.KClass<?> value();
+    property public abstract kotlin.reflect.KClass<?> value;
+  }
+
+  public static final class NavDestination.Companion {
+    method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+    method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+    method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+    method public D build();
+    method public final void deepLink(String uriPattern);
+    method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public final int getId();
+    method public final CharSequence? getLabel();
+    method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+    method public final String? getRoute();
+    method public final void setLabel(CharSequence?);
+    property public final int id;
+    property public final CharSequence? label;
+    property protected final androidx.navigation.Navigator<? extends D> navigator;
+    property public final String? route;
+  }
+
+  @kotlin.DslMarker public @interface NavDestinationDsl {
+  }
+
+  public interface NavDirections {
+    method @IdRes public int getActionId();
+    method public android.os.Bundle getArguments();
+    property @IdRes public abstract int actionId;
+    property public abstract android.os.Bundle arguments;
+  }
+
+  public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+    method public final void addAll(androidx.navigation.NavGraph other);
+    method public final void addDestination(androidx.navigation.NavDestination node);
+    method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+    method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+    method public final void clear();
+    method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+    method public final androidx.navigation.NavDestination? findNode(String? route);
+    method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+    method @Deprecated @IdRes public final int getStartDestination();
+    method @IdRes public final int getStartDestinationId();
+    method public final String? getStartDestinationRoute();
+    method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+    method public final void remove(androidx.navigation.NavDestination node);
+    method public final void setStartDestination(int startDestId);
+    method public final void setStartDestination(String startDestRoute);
+    property @IdRes public final int startDestinationId;
+    property public final String? startDestinationRoute;
+    field public static final androidx.navigation.NavGraph.Companion Companion;
+  }
+
+  public static final class NavGraph.Companion {
+    method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+    method public final void addDestination(androidx.navigation.NavDestination destination);
+    method public androidx.navigation.NavGraph build();
+    method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+    method public final androidx.navigation.NavigatorProvider getProvider();
+    method public final operator void unaryPlus(androidx.navigation.NavDestination);
+    property public final androidx.navigation.NavigatorProvider provider;
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavGraphKt {
+    method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+    method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+    method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+    ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph createDestination();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  public final class NavOptions {
+    method @AnimRes @AnimatorRes public int getEnterAnim();
+    method @AnimRes @AnimatorRes public int getExitAnim();
+    method @AnimRes @AnimatorRes public int getPopEnterAnim();
+    method @AnimRes @AnimatorRes public int getPopExitAnim();
+    method @Deprecated @IdRes public int getPopUpTo();
+    method @IdRes public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean isPopUpToInclusive();
+    method public boolean shouldLaunchSingleTop();
+    method public boolean shouldPopUpToSaveState();
+    method public boolean shouldRestoreState();
+    property @AnimRes @AnimatorRes public final int enterAnim;
+    property @AnimRes @AnimatorRes public final int exitAnim;
+    property @AnimRes @AnimatorRes public final int popEnterAnim;
+    property @AnimRes @AnimatorRes public final int popExitAnim;
+    property @IdRes public final int popUpToId;
+    property public final String? popUpToRoute;
+  }
+
+  public static final class NavOptions.Builder {
+    ctor public NavOptions.Builder();
+    method public androidx.navigation.NavOptions build();
+    method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+    method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+    method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+    method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+    method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+    ctor public NavOptionsBuilder();
+    method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+    method public boolean getLaunchSingleTop();
+    method @Deprecated public int getPopUpTo();
+    method public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean getRestoreState();
+    method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void setLaunchSingleTop(boolean);
+    method @Deprecated public void setPopUpTo(int);
+    method public void setRestoreState(boolean);
+    property public final boolean launchSingleTop;
+    property @Deprecated public final int popUpTo;
+    property public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final boolean restoreState;
+  }
+
+  public final class NavOptionsBuilderKt {
+    method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+  }
+
+  @kotlin.DslMarker public @interface NavOptionsDsl {
+  }
+
+  public abstract class NavType<T> {
+    ctor public NavType(boolean isNullableAllowed);
+    method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+    method public abstract operator T? get(android.os.Bundle bundle, String key);
+    method public String getName();
+    method public boolean isNullableAllowed();
+    method public abstract T parseValue(String value);
+    method public T parseValue(String value, T previousValue);
+    method public abstract void put(android.os.Bundle bundle, String key, T value);
+    method public String serializeAsValue(T value);
+    property public boolean isNullableAllowed;
+    property public String name;
+    field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+    field public static final androidx.navigation.NavType.Companion Companion;
+    field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+    field public static final androidx.navigation.NavType<int[]> IntArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+    field public static final androidx.navigation.NavType<long[]> LongArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+    field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+    field public static final androidx.navigation.NavType<java.lang.String> StringType;
+  }
+
+  public static final class NavType.Companion {
+    method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+  }
+
+  public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+    ctor public NavType.EnumType(Class<D> type);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.ParcelableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+    ctor public NavType.ParcelableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.SerializableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+    ctor public NavType.SerializableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+    ctor public Navigator();
+    method public abstract D createDestination();
+    method protected final androidx.navigation.NavigatorState getState();
+    method public final boolean isAttached();
+    method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+    method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void onRestoreState(android.os.Bundle savedState);
+    method public android.os.Bundle? onSaveState();
+    method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+    method public boolean popBackStack();
+    property public final boolean isAttached;
+    property protected final androidx.navigation.NavigatorState state;
+  }
+
+  public static interface Navigator.Extras {
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+    method public abstract String value();
+    property public abstract String value;
+  }
+
+  public class NavigatorProvider {
+    ctor public NavigatorProvider();
+    method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+    method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+  }
+
+  public final class NavigatorProviderKt {
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+    method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+  }
+
+  public abstract class NavigatorState {
+    ctor public NavigatorState();
+    method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+    ctor public PopUpToBuilder();
+    method public boolean getInclusive();
+    method public boolean getSaveState();
+    method public void setInclusive(boolean);
+    method public void setSaveState(boolean);
+    property public final boolean inclusive;
+    property public final boolean saveState;
+  }
+
+}
+
diff --git a/navigation/navigation-common/api/res-2.6.0-beta02.txt b/navigation/navigation-common/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-common/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-common/api/restricted_2.6.0-beta02.txt b/navigation/navigation-common/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+    ctor public ActionOnlyNavDirections(int actionId);
+    method public int component1();
+    method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+    method public int getActionId();
+    method public android.os.Bundle getArguments();
+    property public int actionId;
+    property public android.os.Bundle arguments;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+    ctor public AnimBuilder();
+    method public int getEnter();
+    method public int getExit();
+    method public int getPopEnter();
+    method public int getPopExit();
+    method public void setEnter(int);
+    method public void setExit(int);
+    method public void setPopEnter(int);
+    method public void setPopExit(int);
+    property public final int enter;
+    property public final int exit;
+    property public final int popEnter;
+    property public final int popExit;
+  }
+
+  public interface FloatingWindow {
+  }
+
+  public final class NamedNavArgument {
+    method public operator String component1();
+    method public operator androidx.navigation.NavArgument component2();
+    method public androidx.navigation.NavArgument getArgument();
+    method public String getName();
+    property public final androidx.navigation.NavArgument argument;
+    property public final String name;
+  }
+
+  public final class NamedNavArgumentKt {
+    method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavAction {
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+    ctor public NavAction(@IdRes int destinationId);
+    method public android.os.Bundle? getDefaultArguments();
+    method public int getDestinationId();
+    method public androidx.navigation.NavOptions? getNavOptions();
+    method public void setDefaultArguments(android.os.Bundle?);
+    method public void setNavOptions(androidx.navigation.NavOptions?);
+    property public final android.os.Bundle? defaultArguments;
+    property public final int destinationId;
+    property public final androidx.navigation.NavOptions? navOptions;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+    ctor public NavActionBuilder();
+    method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+    method public int getDestinationId();
+    method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+    method public void setDestinationId(int);
+    property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+    property public final int destinationId;
+  }
+
+  public interface NavArgs {
+  }
+
+  public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+    ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+    method public Args getValue();
+    method public boolean isInitialized();
+    property public Args value;
+  }
+
+  public final class NavArgument {
+    method public Object? getDefaultValue();
+    method public androidx.navigation.NavType<java.lang.Object> getType();
+    method public boolean isDefaultValuePresent();
+    method public boolean isNullable();
+    property public final Object? defaultValue;
+    property public final boolean isDefaultValuePresent;
+    property public final boolean isNullable;
+    property public final androidx.navigation.NavType<java.lang.Object> type;
+  }
+
+  public static final class NavArgument.Builder {
+    ctor public NavArgument.Builder();
+    method public androidx.navigation.NavArgument build();
+    method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+    method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+    method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+    ctor public NavArgumentBuilder();
+    method public androidx.navigation.NavArgument build();
+    method public Object? getDefaultValue();
+    method public boolean getNullable();
+    method public androidx.navigation.NavType<?> getType();
+    method public void setDefaultValue(Object?);
+    method public void setNullable(boolean);
+    method public void setType(androidx.navigation.NavType<?>);
+    property public final Object? defaultValue;
+    property public final boolean nullable;
+    property public final androidx.navigation.NavType<?> type;
+  }
+
+  public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    method public android.os.Bundle? getArguments();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.navigation.NavDestination getDestination();
+    method public String getId();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    property public final android.os.Bundle? arguments;
+    property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public final androidx.navigation.NavDestination destination;
+    property public final String id;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+  }
+
+  public final class NavDeepLink {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public static final class NavDeepLink.Builder {
+    method public androidx.navigation.NavDeepLink build();
+    method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+    method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+  }
+
+  @kotlin.DslMarker public @interface NavDeepLinkDsl {
+  }
+
+  @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+    ctor public NavDeepLinkDslBuilder();
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    method public void setAction(String?);
+    method public void setMimeType(String?);
+    method public void setUriPattern(String?);
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public final class NavDeepLinkDslBuilderKt {
+    method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+  }
+
+  public class NavDeepLinkRequest {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public android.net.Uri? getUri();
+    property public String? action;
+    property public String? mimeType;
+    property public android.net.Uri? uri;
+  }
+
+  public static final class NavDeepLinkRequest.Builder {
+    method public androidx.navigation.NavDeepLinkRequest build();
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+    field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+  }
+
+  public static final class NavDeepLinkRequest.Builder.Companion {
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+  }
+
+  public class NavDestination {
+    ctor public NavDestination(String navigatorName);
+    ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+    method public final void addDeepLink(String uriPattern);
+    method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+    method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+    method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+    method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method @IdRes public final int getId();
+    method public final CharSequence? getLabel();
+    method public final String getNavigatorName();
+    method public final androidx.navigation.NavGraph? getParent();
+    method public final String? getRoute();
+    method public boolean hasDeepLink(android.net.Uri deepLink);
+    method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+    method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+    method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+    method public final void putAction(@IdRes int actionId, @IdRes int destId);
+    method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+    method public final void removeAction(@IdRes int actionId);
+    method public final void removeArgument(String argumentName);
+    method public final void setId(@IdRes int);
+    method public final void setLabel(CharSequence?);
+    method public final void setRoute(String?);
+    property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+    property @IdRes public final int id;
+    property public final CharSequence? label;
+    property public final String navigatorName;
+    property public final androidx.navigation.NavGraph? parent;
+    property public final String? route;
+    field public static final androidx.navigation.NavDestination.Companion Companion;
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+    method public abstract kotlin.reflect.KClass<?> value();
+    property public abstract kotlin.reflect.KClass<?> value;
+  }
+
+  public static final class NavDestination.Companion {
+    method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+    method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+    method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+    method public D build();
+    method public final void deepLink(String uriPattern);
+    method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public final int getId();
+    method public final CharSequence? getLabel();
+    method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+    method public final String? getRoute();
+    method public final void setLabel(CharSequence?);
+    property public final int id;
+    property public final CharSequence? label;
+    property protected final androidx.navigation.Navigator<? extends D> navigator;
+    property public final String? route;
+  }
+
+  @kotlin.DslMarker public @interface NavDestinationDsl {
+  }
+
+  public interface NavDirections {
+    method @IdRes public int getActionId();
+    method public android.os.Bundle getArguments();
+    property @IdRes public abstract int actionId;
+    property public abstract android.os.Bundle arguments;
+  }
+
+  public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+    method public final void addAll(androidx.navigation.NavGraph other);
+    method public final void addDestination(androidx.navigation.NavDestination node);
+    method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+    method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+    method public final void clear();
+    method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+    method public final androidx.navigation.NavDestination? findNode(String? route);
+    method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+    method @Deprecated @IdRes public final int getStartDestination();
+    method @IdRes public final int getStartDestinationId();
+    method public final String? getStartDestinationRoute();
+    method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+    method public final void remove(androidx.navigation.NavDestination node);
+    method public final void setStartDestination(int startDestId);
+    method public final void setStartDestination(String startDestRoute);
+    property @IdRes public final int startDestinationId;
+    property public final String? startDestinationRoute;
+    field public static final androidx.navigation.NavGraph.Companion Companion;
+  }
+
+  public static final class NavGraph.Companion {
+    method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+    method public final void addDestination(androidx.navigation.NavDestination destination);
+    method public androidx.navigation.NavGraph build();
+    method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+    method public final androidx.navigation.NavigatorProvider getProvider();
+    method public final operator void unaryPlus(androidx.navigation.NavDestination);
+    property public final androidx.navigation.NavigatorProvider provider;
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavGraphKt {
+    method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+    method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+    method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+    ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph createDestination();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  public final class NavOptions {
+    method @AnimRes @AnimatorRes public int getEnterAnim();
+    method @AnimRes @AnimatorRes public int getExitAnim();
+    method @AnimRes @AnimatorRes public int getPopEnterAnim();
+    method @AnimRes @AnimatorRes public int getPopExitAnim();
+    method @Deprecated @IdRes public int getPopUpTo();
+    method @IdRes public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean isPopUpToInclusive();
+    method public boolean shouldLaunchSingleTop();
+    method public boolean shouldPopUpToSaveState();
+    method public boolean shouldRestoreState();
+    property @AnimRes @AnimatorRes public final int enterAnim;
+    property @AnimRes @AnimatorRes public final int exitAnim;
+    property @AnimRes @AnimatorRes public final int popEnterAnim;
+    property @AnimRes @AnimatorRes public final int popExitAnim;
+    property @IdRes public final int popUpToId;
+    property public final String? popUpToRoute;
+  }
+
+  public static final class NavOptions.Builder {
+    ctor public NavOptions.Builder();
+    method public androidx.navigation.NavOptions build();
+    method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+    method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+    method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+    method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+    method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+    ctor public NavOptionsBuilder();
+    method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+    method public boolean getLaunchSingleTop();
+    method @Deprecated public int getPopUpTo();
+    method public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public boolean getRestoreState();
+    method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void setLaunchSingleTop(boolean);
+    method @Deprecated public void setPopUpTo(int);
+    method public void setRestoreState(boolean);
+    property public final boolean launchSingleTop;
+    property @Deprecated public final int popUpTo;
+    property public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final boolean restoreState;
+  }
+
+  public final class NavOptionsBuilderKt {
+    method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+  }
+
+  @kotlin.DslMarker public @interface NavOptionsDsl {
+  }
+
+  public abstract class NavType<T> {
+    ctor public NavType(boolean isNullableAllowed);
+    method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+    method public abstract operator T? get(android.os.Bundle bundle, String key);
+    method public String getName();
+    method public boolean isNullableAllowed();
+    method public abstract T parseValue(String value);
+    method public T parseValue(String value, T previousValue);
+    method public abstract void put(android.os.Bundle bundle, String key, T value);
+    method public String serializeAsValue(T value);
+    property public boolean isNullableAllowed;
+    property public String name;
+    field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+    field public static final androidx.navigation.NavType.Companion Companion;
+    field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+    field public static final androidx.navigation.NavType<int[]> IntArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+    field public static final androidx.navigation.NavType<long[]> LongArrayType;
+    field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+    field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+    field public static final androidx.navigation.NavType<java.lang.String> StringType;
+  }
+
+  public static final class NavType.Companion {
+    method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+  }
+
+  public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+    ctor public NavType.EnumType(Class<D> type);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.ParcelableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+    ctor public NavType.ParcelableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+    ctor public NavType.SerializableArrayType(Class<D> type);
+    method public D![]? get(android.os.Bundle bundle, String key);
+    method public D![] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D![]? value);
+    property public String name;
+  }
+
+  public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+    ctor public NavType.SerializableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+    ctor public Navigator();
+    method public abstract D createDestination();
+    method protected final androidx.navigation.NavigatorState getState();
+    method public final boolean isAttached();
+    method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+    method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void onRestoreState(android.os.Bundle savedState);
+    method public android.os.Bundle? onSaveState();
+    method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+    method public boolean popBackStack();
+    property public final boolean isAttached;
+    property protected final androidx.navigation.NavigatorState state;
+  }
+
+  public static interface Navigator.Extras {
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+    method public abstract String value();
+    property public abstract String value;
+  }
+
+  public class NavigatorProvider {
+    ctor public NavigatorProvider();
+    method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+    method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+  }
+
+  public final class NavigatorProviderKt {
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+    method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+  }
+
+  public abstract class NavigatorState {
+    ctor public NavigatorState();
+    method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+    ctor public PopUpToBuilder();
+    method public boolean getInclusive();
+    method public boolean getSaveState();
+    method public void setInclusive(boolean);
+    method public void setSaveState(boolean);
+    property public final boolean inclusive;
+    property public final boolean saveState;
+  }
+
+}
+
diff --git a/navigation/navigation-common/api/restricted_current.ignore b/navigation/navigation-common/api/restricted_current.ignore
deleted file mode 100644
index 8eebd5d..0000000
--- a/navigation/navigation-common/api/restricted_current.ignore
+++ /dev/null
@@ -1,9 +0,0 @@
-// Baseline format: 1.0
-InvalidNullConversion: androidx.navigation.NavType#put(android.os.Bundle, String, T) parameter #2:
-    Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.put(android.os.Bundle bundle, String key, T value)
-InvalidNullConversion: androidx.navigation.NavType.ParcelableType#put(android.os.Bundle, String, D) parameter #2:
-    Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.ParcelableType.put(android.os.Bundle bundle, String key, D value)
-
-
-RemovedClass: androidx.navigation.NavArgsLazyKt:
-    Removed class androidx.navigation.NavArgsLazyKt
diff --git a/navigation/navigation-compose/api/2.6.0-beta02.txt b/navigation/navigation-compose/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigator();
+    method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+    method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class DialogHostKt {
+    method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigator();
+    method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class NavBackStackEntryProviderKt {
+    method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class NavGraphBuilderKt {
+    method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostControllerKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+    method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+  }
+
+  public final class NavHostKt {
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+  }
+
+}
+
diff --git a/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigator();
+    method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+    method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class DialogHostKt {
+    method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigator();
+    method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class NavBackStackEntryProviderKt {
+    method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class NavGraphBuilderKt {
+    method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostControllerKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+    method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+  }
+
+  public final class NavHostKt {
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+  }
+
+}
+
diff --git a/navigation/navigation-compose/api/res-2.6.0-beta02.txt b/navigation/navigation-compose/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-compose/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt b/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigator();
+    method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+    method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class DialogHostKt {
+    method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigator();
+    method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  public final class NavBackStackEntryProviderKt {
+    method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class NavGraphBuilderKt {
+    method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostControllerKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+    method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+  }
+
+  public final class NavHostKt {
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+  @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+    ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  public final class DynamicFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+  }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+  public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractProgressFragment();
+    ctor public AbstractProgressFragment(int contentLayoutId);
+    method protected abstract void onCancelled();
+    method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onInstalled();
+    method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+  }
+
+  public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+    ctor public DefaultProgressFragment();
+    method protected void onCancelled();
+    method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+  @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+    ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  public final class DynamicFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+  }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+  public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractProgressFragment();
+    ctor public AbstractProgressFragment(int contentLayoutId);
+    method protected abstract void onCancelled();
+    method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onInstalled();
+    method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+  }
+
+  public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+    ctor public DefaultProgressFragment();
+    method protected void onCancelled();
+    method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+  @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+    ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  public final class DynamicFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+  }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+  public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractProgressFragment();
+    ctor public AbstractProgressFragment(int contentLayoutId);
+    method protected abstract void onCancelled();
+    method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onInstalled();
+    method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+  }
+
+  public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+    ctor public DefaultProgressFragment();
+    method protected void onCancelled();
+    method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+  @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+    ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+    method public String? getAction();
+    method public String? getActivityClassName();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getModuleName();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClassName(String?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setModuleName(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final String? activityClassName;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? moduleName;
+    property public final String? targetPackage;
+  }
+
+  public final class DynamicActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+    ctor public DynamicExtras();
+    method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+    method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+    property public final androidx.navigation.Navigator.Extras? destinationExtras;
+    property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+    ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+    method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+  }
+
+  public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+    ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    property public final String? moduleName;
+    property public final int progressDestination;
+  }
+
+  @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+  }
+
+  public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+    method public String? getGraphPackage();
+    method public String? getGraphResourceName();
+    method public String? getModuleName();
+    method public void setGraphPackage(String?);
+    method public void setGraphResourceName(String?);
+    method public void setModuleName(String?);
+    property public final String? graphPackage;
+    property public final String? graphResourceName;
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+    method public String? getGraphPackage();
+    method public void setGraphPackage(String?);
+    property public final String? graphPackage;
+  }
+
+  public final class DynamicIncludeNavGraphBuilderKt {
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicInstallManager {
+    ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+  }
+
+  public final class DynamicInstallMonitor {
+    ctor public DynamicInstallMonitor();
+    method public void cancelInstall();
+    method public Exception? getException();
+    method public int getSessionId();
+    method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+    method public boolean isInstallRequired();
+    property public final Exception? exception;
+    property public final boolean isInstallRequired;
+    property public final int sessionId;
+    property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public String? getProgressDestinationRoute();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    method public void setProgressDestinationRoute(String?);
+    property public final String? moduleName;
+    property public final int progressDestination;
+    property public final String? progressDestinationRoute;
+  }
+
+  public final class DynamicNavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+  @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+    ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+    method public String? getAction();
+    method public String? getActivityClassName();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getModuleName();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClassName(String?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setModuleName(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final String? activityClassName;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? moduleName;
+    property public final String? targetPackage;
+  }
+
+  public final class DynamicActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+    ctor public DynamicExtras();
+    method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+    method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+    property public final androidx.navigation.Navigator.Extras? destinationExtras;
+    property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+    ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+    method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+  }
+
+  public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+    ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    property public final String? moduleName;
+    property public final int progressDestination;
+  }
+
+  @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+  }
+
+  public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+    method public String? getGraphPackage();
+    method public String? getGraphResourceName();
+    method public String? getModuleName();
+    method public void setGraphPackage(String?);
+    method public void setGraphResourceName(String?);
+    method public void setModuleName(String?);
+    property public final String? graphPackage;
+    property public final String? graphResourceName;
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+    method public String? getGraphPackage();
+    method public void setGraphPackage(String?);
+    property public final String? graphPackage;
+  }
+
+  public final class DynamicIncludeNavGraphBuilderKt {
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicInstallManager {
+    ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+  }
+
+  public final class DynamicInstallMonitor {
+    ctor public DynamicInstallMonitor();
+    method public void cancelInstall();
+    method public Exception? getException();
+    method public int getSessionId();
+    method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+    method public boolean isInstallRequired();
+    property public final Exception? exception;
+    property public final boolean isInstallRequired;
+    property public final int sessionId;
+    property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public String? getProgressDestinationRoute();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    method public void setProgressDestinationRoute(String?);
+    property public final String? moduleName;
+    property public final int progressDestination;
+    property public final String? progressDestinationRoute;
+  }
+
+  public final class DynamicNavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+  @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+    ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+    method public String? getAction();
+    method public String? getActivityClassName();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getModuleName();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClassName(String?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setModuleName(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final String? activityClassName;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? moduleName;
+    property public final String? targetPackage;
+  }
+
+  public final class DynamicActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+    ctor public DynamicExtras();
+    method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+    method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+    property public final androidx.navigation.Navigator.Extras? destinationExtras;
+    property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+    ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+    method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+  }
+
+  public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+    ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    property public final String? moduleName;
+    property public final int progressDestination;
+  }
+
+  @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+  }
+
+  public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+    method public String? getGraphPackage();
+    method public String? getGraphResourceName();
+    method public String? getModuleName();
+    method public void setGraphPackage(String?);
+    method public void setGraphResourceName(String?);
+    method public void setModuleName(String?);
+    property public final String? graphPackage;
+    property public final String? graphResourceName;
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+    method public String? getGraphPackage();
+    method public void setGraphPackage(String?);
+    property public final String? graphPackage;
+  }
+
+  public final class DynamicIncludeNavGraphBuilderKt {
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicInstallManager {
+    ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+  }
+
+  public final class DynamicInstallMonitor {
+    ctor public DynamicInstallMonitor();
+    method public void cancelInstall();
+    method public Exception? getException();
+    method public int getSessionId();
+    method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+    method public boolean isInstallRequired();
+    property public final Exception? exception;
+    property public final boolean isInstallRequired;
+    property public final int sessionId;
+    property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public String? getProgressDestinationRoute();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    method public void setProgressDestinationRoute(String?);
+    property public final String? moduleName;
+    property public final int progressDestination;
+    property public final String? progressDestinationRoute;
+  }
+
+  public final class DynamicNavGraphBuilderKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment/api/2.6.0-beta02.txt b/navigation/navigation-fragment/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class NavGraphViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+}
+
+package androidx.navigation.fragment {
+
+  public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractListDetailFragment();
+    method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+    method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+    method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+    method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+    property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+  }
+
+  public final class DialogFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentKt {
+    method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+  }
+
+  public final class FragmentNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+  }
+
+  @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+    method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+    property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+  }
+
+  public static final class FragmentNavigator.Extras.Builder {
+    ctor public FragmentNavigator.Extras.Builder();
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+  }
+
+  public final class FragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentNavigatorExtrasKt {
+    method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+  }
+
+  public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+    ctor public NavHostFragment();
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+    method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+    method public final androidx.navigation.NavController getNavController();
+    method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+    method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+    property public final androidx.navigation.NavController navController;
+    field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+  }
+
+  public static final class NavHostFragment.Companion {
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+  }
+
+}
+
diff --git a/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class NavGraphViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+}
+
+package androidx.navigation.fragment {
+
+  public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractListDetailFragment();
+    method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+    method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+    method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+    method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+    property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+  }
+
+  public final class DialogFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentKt {
+    method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+  }
+
+  public final class FragmentNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+  }
+
+  @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+    method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+    property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+  }
+
+  public static final class FragmentNavigator.Extras.Builder {
+    ctor public FragmentNavigator.Extras.Builder();
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+  }
+
+  public final class FragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentNavigatorExtrasKt {
+    method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+  }
+
+  public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+    ctor public NavHostFragment();
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+    method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+    method public final androidx.navigation.NavController getNavController();
+    method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+    method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+    property public final androidx.navigation.NavController navController;
+    field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+  }
+
+  public static final class NavHostFragment.Companion {
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+  }
+
+}
+
diff --git a/navigation/navigation-fragment/api/res-2.6.0-beta02.txt b/navigation/navigation-fragment/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-fragment/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt b/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class NavGraphViewModelLazyKt {
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+}
+
+package androidx.navigation.fragment {
+
+  public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractListDetailFragment();
+    method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+    method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+    method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+    method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+    property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+  }
+
+  public final class DialogFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentKt {
+    method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+  }
+
+  public final class FragmentNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+  }
+
+  @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+    method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+    property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+  }
+
+  public static final class FragmentNavigator.Extras.Builder {
+    ctor public FragmentNavigator.Extras.Builder();
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+  }
+
+  public final class FragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+  }
+
+  public final class FragmentNavigatorExtrasKt {
+    method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+  }
+
+  public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+    ctor public NavHostFragment();
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+    method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+    method public final androidx.navigation.NavController getNavController();
+    method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+    method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+    property public final androidx.navigation.NavController navController;
+    field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+  }
+
+  public static final class NavHostFragment.Companion {
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+  }
+
+}
+
diff --git a/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime/api/2.6.0-beta02.txt b/navigation/navigation-runtime/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..39d9a7f
--- /dev/null
+++ b/navigation/navigation-runtime/api/2.6.0-beta02.txt
@@ -0,0 +1,224 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActivityKt {
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+  }
+
+  public final class ActivityNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+  }
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context context);
+    method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+  }
+
+  public static final class ActivityNavigator.Companion {
+    method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+    property public final String? action;
+    property public final android.content.ComponentName? component;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final android.content.Intent? intent;
+    property public final String? targetPackage;
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+    property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+    property public final int flags;
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+    method public androidx.navigation.ActivityNavigator.Destination build();
+    method public String? getAction();
+    method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? targetPackage;
+  }
+
+  public final class ActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class ActivityNavigatorExtrasKt {
+    method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @MainThread public final boolean clearBackStack(String route);
+    method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+    method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+    method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+    method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method @MainThread public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+    method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+    method @MainThread public void navigate(@IdRes int resId);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(android.net.Uri deepLink);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+    method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final void navigate(String route);
+    method @MainThread public boolean navigateUp();
+    method @MainThread public boolean popBackStack();
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @CallSuper public void restoreState(android.os.Bundle? navState);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+    property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+    property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+    property public androidx.navigation.NavDestination? currentDestination;
+    property @MainThread public androidx.navigation.NavGraph graph;
+    property public androidx.navigation.NavInflater navInflater;
+    property public androidx.navigation.NavigatorProvider navigatorProvider;
+    property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+    field public static final androidx.navigation.NavController.Companion Companion;
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static final class NavController.Companion {
+  }
+
+  public static fun interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context context);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+    property public abstract androidx.navigation.NavController navController;
+  }
+
+  public class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context context);
+    method public final void enableOnBackPressed(boolean enabled);
+    method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+    method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+    method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+    field public static final androidx.navigation.NavInflater.Companion Companion;
+  }
+
+  public static final class NavInflater.Companion {
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+    method public static androidx.navigation.NavController findNavController(android.view.View view);
+    method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+    field public static final androidx.navigation.Navigation INSTANCE;
+  }
+
+  public final class ViewKt {
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+  }
+
+}
+
diff --git a/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..6faf37c
--- /dev/null
+++ b/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,229 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActivityKt {
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+  }
+
+  public final class ActivityNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+  }
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context context);
+    method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+  }
+
+  public static final class ActivityNavigator.Companion {
+    method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+    property public final String? action;
+    property public final android.content.ComponentName? component;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final android.content.Intent? intent;
+    property public final String? targetPackage;
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+    property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+    property public final int flags;
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+    method public androidx.navigation.ActivityNavigator.Destination build();
+    method public String? getAction();
+    method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? targetPackage;
+  }
+
+  public final class ActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class ActivityNavigatorExtrasKt {
+    method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @MainThread public final boolean clearBackStack(String route);
+    method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method @androidx.navigation.NavDeepLinkSaveStateControl public static final void enableDeepLinkSaveState(boolean saveState);
+    method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+    method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+    method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+    method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method @MainThread public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+    method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+    method @MainThread public void navigate(@IdRes int resId);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(android.net.Uri deepLink);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+    method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final void navigate(String route);
+    method @MainThread public boolean navigateUp();
+    method @MainThread public boolean popBackStack();
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @CallSuper public void restoreState(android.os.Bundle? navState);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+    property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+    property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+    property public androidx.navigation.NavDestination? currentDestination;
+    property @MainThread public androidx.navigation.NavGraph graph;
+    property public androidx.navigation.NavInflater navInflater;
+    property public androidx.navigation.NavigatorProvider navigatorProvider;
+    property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+    field public static final androidx.navigation.NavController.Companion Companion;
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static final class NavController.Companion {
+    method @androidx.navigation.NavDeepLinkSaveStateControl public void enableDeepLinkSaveState(boolean saveState);
+  }
+
+  public static fun interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context context);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+  }
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavDeepLinkSaveStateControl {
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+    property public abstract androidx.navigation.NavController navController;
+  }
+
+  public class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context context);
+    method public final void enableOnBackPressed(boolean enabled);
+    method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+    method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+    method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+    field public static final androidx.navigation.NavInflater.Companion Companion;
+  }
+
+  public static final class NavInflater.Companion {
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+    method public static androidx.navigation.NavController findNavController(android.view.View view);
+    method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+    field public static final androidx.navigation.Navigation INSTANCE;
+  }
+
+  public final class ViewKt {
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+  }
+
+}
+
diff --git a/navigation/navigation-runtime/api/res-2.6.0-beta02.txt b/navigation/navigation-runtime/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-runtime/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt b/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..39d9a7f
--- /dev/null
+++ b/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,224 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActivityKt {
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+  }
+
+  public final class ActivityNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+  }
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context context);
+    method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+  }
+
+  public static final class ActivityNavigator.Companion {
+    method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+    property public final String? action;
+    property public final android.content.ComponentName? component;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final android.content.Intent? intent;
+    property public final String? targetPackage;
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+    property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+    property public final int flags;
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+    method public androidx.navigation.ActivityNavigator.Destination build();
+    method public String? getAction();
+    method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? targetPackage;
+  }
+
+  public final class ActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class ActivityNavigatorExtrasKt {
+    method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @MainThread public final boolean clearBackStack(String route);
+    method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+    method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+    method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+    method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method @MainThread public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+    method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+    method @MainThread public void navigate(@IdRes int resId);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(android.net.Uri deepLink);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+    method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final void navigate(String route);
+    method @MainThread public boolean navigateUp();
+    method @MainThread public boolean popBackStack();
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @CallSuper public void restoreState(android.os.Bundle? navState);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+    property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+    property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+    property public androidx.navigation.NavDestination? currentDestination;
+    property @MainThread public androidx.navigation.NavGraph graph;
+    property public androidx.navigation.NavInflater navInflater;
+    property public androidx.navigation.NavigatorProvider navigatorProvider;
+    property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+    field public static final androidx.navigation.NavController.Companion Companion;
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static final class NavController.Companion {
+  }
+
+  public static fun interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context context);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+    property public abstract androidx.navigation.NavController navController;
+  }
+
+  public class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context context);
+    method public final void enableOnBackPressed(boolean enabled);
+    method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+    method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+    method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+    field public static final androidx.navigation.NavInflater.Companion Companion;
+  }
+
+  public static final class NavInflater.Companion {
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+    method public static androidx.navigation.NavController findNavController(android.view.View view);
+    method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+    field public static final androidx.navigation.Navigation INSTANCE;
+  }
+
+  public final class ViewKt {
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+  }
+
+}
+
diff --git a/navigation/navigation-testing/api/2.6.0-beta02.txt b/navigation/navigation-testing/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+  public final class TestNavHostController extends androidx.navigation.NavHostController {
+    ctor public TestNavHostController(android.content.Context context);
+    method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+    method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+    method public void setCurrentDestination(@IdRes int destId);
+    method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+    method public void setCurrentDestination(String destRoute);
+    property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+  }
+
+  public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+    ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+    ctor public TestNavigatorState(optional android.content.Context? context);
+    ctor public TestNavigatorState();
+    method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+  }
+
+}
+
diff --git a/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+  public final class TestNavHostController extends androidx.navigation.NavHostController {
+    ctor public TestNavHostController(android.content.Context context);
+    method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+    method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+    method public void setCurrentDestination(@IdRes int destId);
+    method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+    method public void setCurrentDestination(String destRoute);
+    property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+  }
+
+  public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+    ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+    ctor public TestNavigatorState(optional android.content.Context? context);
+    ctor public TestNavigatorState();
+    method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+  }
+
+}
+
diff --git a/navigation/navigation-testing/api/res-2.6.0-beta02.txt b/navigation/navigation-testing/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-testing/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt b/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+  public final class TestNavHostController extends androidx.navigation.NavHostController {
+    ctor public TestNavHostController(android.content.Context context);
+    method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+    method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+    method public void setCurrentDestination(@IdRes int destId);
+    method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+    method public void setCurrentDestination(String destRoute);
+    property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+  }
+
+  public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+    ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+    ctor public TestNavigatorState(optional android.content.Context? context);
+    ctor public TestNavigatorState();
+    method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+  }
+
+}
+
diff --git a/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui/api/2.6.0-beta02.txt b/navigation/navigation-ui/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..27e99a9
--- /dev/null
+++ b/navigation/navigation-ui/api/2.6.0-beta02.txt
@@ -0,0 +1,88 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+  public final class ActivityKt {
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class AppBarConfiguration {
+    method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+    method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+    method public androidx.customview.widget.Openable? getOpenableLayout();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+  }
+
+  public static final class AppBarConfiguration.Builder {
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+    method public androidx.navigation.ui.AppBarConfiguration build();
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+  }
+
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
+    method public boolean onNavigateUp();
+  }
+
+  public final class AppBarConfigurationKt {
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+  }
+
+  public final class BottomNavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+  }
+
+  public final class CollapsingToolbarLayoutKt {
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class MenuItemKt {
+    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+  }
+
+  public final class NavControllerKt {
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+  }
+
+  public final class NavigationUI {
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+  }
+
+  public final class NavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+  }
+
+  public final class ToolbarKt {
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+}
+
diff --git a/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..7cb97e2
--- /dev/null
+++ b/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+  public final class ActivityKt {
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class AppBarConfiguration {
+    method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+    method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+    method public androidx.customview.widget.Openable? getOpenableLayout();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+  }
+
+  public static final class AppBarConfiguration.Builder {
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+    method public androidx.navigation.ui.AppBarConfiguration build();
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+  }
+
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
+    method public boolean onNavigateUp();
+  }
+
+  public final class AppBarConfigurationKt {
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+  }
+
+  public final class BottomNavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+  }
+
+  public final class CollapsingToolbarLayoutKt {
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class MenuItemKt {
+    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+  }
+
+  public final class NavControllerKt {
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+  }
+
+  public final class NavigationUI {
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method @androidx.navigation.ui.NavigationUiSaveStateControl public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+    method @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController, boolean saveState);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+  }
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavigationUiSaveStateControl {
+  }
+
+  public final class NavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+  }
+
+  public final class ToolbarKt {
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+}
+
diff --git a/navigation/navigation-ui/api/res-2.6.0-beta02.txt b/navigation/navigation-ui/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e65fdbe
--- /dev/null
+++ b/navigation/navigation-ui/api/res-2.6.0-beta02.txt
@@ -0,0 +1,8 @@
+anim nav_default_enter_anim
+anim nav_default_exit_anim
+anim nav_default_pop_enter_anim
+anim nav_default_pop_exit_anim
+animator nav_default_enter_anim
+animator nav_default_exit_anim
+animator nav_default_pop_enter_anim
+animator nav_default_pop_exit_anim
diff --git a/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt b/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..27e99a9
--- /dev/null
+++ b/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,88 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+  public final class ActivityKt {
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class AppBarConfiguration {
+    method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+    method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+    method public androidx.customview.widget.Openable? getOpenableLayout();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+  }
+
+  public static final class AppBarConfiguration.Builder {
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+    method public androidx.navigation.ui.AppBarConfiguration build();
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+  }
+
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
+    method public boolean onNavigateUp();
+  }
+
+  public final class AppBarConfigurationKt {
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+  }
+
+  public final class BottomNavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+  }
+
+  public final class CollapsingToolbarLayoutKt {
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class MenuItemKt {
+    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+  }
+
+  public final class NavControllerKt {
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+  }
+
+  public final class NavigationUI {
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+  }
+
+  public final class NavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+  }
+
+  public final class ToolbarKt {
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+}
+
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
index 0955880..78598f9 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
@@ -27,20 +27,22 @@
 import androidx.paging.PagingSource.LoadParams.Refresh
 import androidx.paging.PagingSource.LoadResult
 import androidx.paging.PagingSource.LoadResult.Page
-import androidx.testutils.TestDispatcher
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(JUnit4::class)
 class LegacyPageFetcherTest {
-    private val testDispatcher = TestDispatcher()
+    private val testDispatcher = StandardTestDispatcher()
     private val data = List(9) { "$it" }
 
     inner class ImmediateListDataSource(val data: List<String>) : PagingSource<Int, String>() {
@@ -177,7 +179,7 @@
             consumer.takeStateChanges()
         )
 
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(listOf(Result(APPEND, rangeResult(6, 8))), consumer.takeResults())
         assertEquals(
@@ -204,7 +206,7 @@
             consumer.takeStateChanges()
         )
 
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(
             listOf(Result(PREPEND, rangeResult(2, 4))),
@@ -227,7 +229,7 @@
         val pager = createPager(consumer, 2, 6)
 
         pager.tryScheduleAppend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(
             listOf(
@@ -248,7 +250,7 @@
         )
 
         pager.tryScheduleAppend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(
             listOf(
@@ -275,7 +277,7 @@
         val pager = createPager(consumer, 4, 8)
 
         pager.trySchedulePrepend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(
             listOf(
@@ -295,7 +297,7 @@
         )
 
         pager.trySchedulePrepend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertEquals(
             listOf(
@@ -364,7 +366,7 @@
 
         // try a normal append first
         pager.tryScheduleAppend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(consumer.takeResults()).containsExactly(
             Result(APPEND, rangeResult(3, 5))
@@ -379,7 +381,7 @@
         pagingSource.invalidData = true
 
         pager.tryScheduleAppend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         // the load should return before returning any data
         assertThat(consumer.takeResults()).isEmpty()
@@ -400,7 +402,7 @@
 
         // try a normal prepend first
         pager.trySchedulePrepend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(consumer.takeResults()).containsExactly(
             Result(PREPEND, rangeResult(4, 6))
@@ -415,7 +417,7 @@
         pagingSource.invalidData = true
 
         pager.trySchedulePrepend()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
 
         // the load should return before returning any data
         assertThat(consumer.takeResults()).isEmpty()
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
index ceb1e7f..1ffe862 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
@@ -17,7 +17,6 @@
 package androidx.paging
 
 import androidx.paging.PagingSource.LoadResult.Page
-import androidx.testutils.TestDispatcher
 import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.coroutines.Dispatchers
@@ -27,6 +26,7 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.take
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
 import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,7 +35,9 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(JUnit4::class)
 class LegacyPagingSourceTest {
     private val fakePagingState = PagingState(
@@ -357,7 +359,7 @@
             }
         }
 
-        val testDispatcher = TestDispatcher()
+        val testDispatcher = StandardTestDispatcher()
         val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory(
             fetchDispatcher = testDispatcher
         ).let {
@@ -365,14 +367,14 @@
         }
 
         val pagingSource0 = pagingSourceFactory()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
         assertTrue { pagingSource0.dataSource.isInvalid }
         assertTrue { pagingSource0.invalid }
         assertTrue { dataSourceFactory.dataSources[0].isInvalid }
         assertEquals(dataSourceFactory.dataSources[0], pagingSource0.dataSource)
 
         val pagingSource1 = pagingSourceFactory()
-        testDispatcher.executeAll()
+        testDispatcher.scheduler.advanceUntilIdle()
         assertFalse { pagingSource1.dataSource.isInvalid }
         assertFalse { pagingSource1.invalid }
         assertFalse { dataSourceFactory.dataSources[1].isInvalid }
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 8c62bec..6f89ef0 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.paging
 
-import androidx.testutils.TestDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,12 +32,11 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.test.assertFailsWith
+import kotlinx.coroutines.test.StandardTestDispatcher
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(JUnit4::class)
 class PageKeyedDataSourceTest {
-    private val mainThread = TestDispatcher()
-    private val backgroundThread = TestDispatcher()
-
     internal data class Item(val name: String)
 
     internal data class Page(val prev: String?, val data: List<Item>, val next: String?)
@@ -244,7 +242,7 @@
         @Suppress("UNCHECKED_CAST", "DEPRECATION")
         val boundaryCallback =
             mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
-        val dispatcher = TestDispatcher()
+        val dispatcher = StandardTestDispatcher()
 
         val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
         @Suppress("DEPRECATION")
@@ -259,7 +257,7 @@
 
         verifyNoMoreInteractions(boundaryCallback)
 
-        dispatcher.executeAll()
+        dispatcher.scheduler.advanceUntilIdle()
 
         // verify boundary callbacks are triggered
         verify(boundaryCallback).onItemAtFrontLoaded("A")
@@ -297,7 +295,7 @@
         @Suppress("UNCHECKED_CAST", "DEPRECATION")
         val boundaryCallback =
             mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
-        val dispatcher = TestDispatcher()
+        val dispatcher = StandardTestDispatcher()
 
         val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
         @Suppress("DEPRECATION")
@@ -312,7 +310,7 @@
 
         verifyNoMoreInteractions(boundaryCallback)
 
-        dispatcher.executeAll()
+        dispatcher.scheduler.advanceUntilIdle()
 
         // verify boundary callbacks are triggered
         verify(boundaryCallback).onItemAtFrontLoaded("B")
@@ -512,11 +510,4 @@
             ITEM_LIST = list
         }
     }
-
-    private fun drain() {
-        while (backgroundThread.queue.isNotEmpty() || mainThread.queue.isNotEmpty()) {
-            backgroundThread.executeAll()
-            mainThread.executeAll()
-        }
-    }
 }
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
index 9253657..adca804 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
@@ -22,7 +22,6 @@
 import androidx.paging.PageEvent.Drop
 import androidx.paging.PagingSource.LoadResult
 import androidx.testutils.MainDispatcherRule
-import androidx.testutils.TestDispatcher
 import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.ContinuationInterceptor
 import kotlin.test.assertEquals
@@ -47,6 +46,8 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.advanceTimeBy
@@ -261,12 +262,12 @@
     fun refreshOnLatestGenerationReceiver() = runTest { differ, loadDispatcher, _,
         uiReceivers, hintReceivers ->
         // first gen
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
 
         // append a page so we can cache an anchorPosition of [8]
         differ[8]
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 12)
 
@@ -279,7 +280,7 @@
         differ.refresh()
         assertThat(uiReceivers[0].refreshEvents).hasSize(1)
         assertThat(uiReceivers[1].refreshEvents).hasSize(1)
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(8 until 17)
 
@@ -301,12 +302,12 @@
         uiReceivers, hintReceivers ->
 
         // first gen
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
 
         // append a page so we can cache an anchorPosition of [8]
         differ[8]
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 12)
 
@@ -317,7 +318,7 @@
 
         // to recreate a real use-case of retry based on load error
         pagingSources[1].errorNextLoad = true
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
         // differ should still have first gen presenter
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 12)
 
@@ -325,7 +326,7 @@
         differ.retry()
         assertThat(uiReceivers[0].retryEvents).hasSize(0)
         assertThat(uiReceivers[1].retryEvents).hasSize(1)
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // will retry with the correct cached hint
         assertThat(differ.snapshot()).containsExactlyElementsIn(8 until 17)
@@ -1466,7 +1467,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // execute queued initial REFRESH
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(50 until 59)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1477,7 +1478,7 @@
         differ.refresh()
 
         // execute second REFRESH load
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // second refresh still loads from initialKey = 50 because anchorPosition/refreshKey is null
         assertThat(pagingSources.size).isEqualTo(2)
@@ -1498,7 +1499,7 @@
         }
         val collectLoadStates = differ.collectLoadStates()
         // execute initial refresh
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(
@@ -1513,7 +1514,7 @@
         differ.refresh()
         // after a refresh, make sure the loading event comes in 1 piece w/ the end of pagination
         // reset
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(
                 refreshLocal = Loading,
@@ -1546,7 +1547,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // initial REFRESH
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1557,7 +1558,7 @@
         // normal append
         differ[8]
 
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 12)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1572,9 +1573,9 @@
         differ[11]
         pagingSources[0].nextLoadResult = LoadResult.Invalid()
 
-        // using poll().run() instead of executeAll, otherwise this invalid APPEND + subsequent
+        // using advanceTimeBy instead of advanceUntilIdle, otherwise this invalid APPEND + subsequent
         // REFRESH will auto run consecutively and we won't be able to assert them incrementally
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceTimeBy(1001)
 
         assertThat(pagingSources.size).isEqualTo(2)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1592,7 +1593,7 @@
         )
 
         // the LoadResult.Invalid from failed APPEND triggers new pagingSource + initial REFRESH
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(11 until 20)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1608,7 +1609,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // initial REFRESH
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(50 until 59)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1620,7 +1621,7 @@
         // normal prepend to ensure LoadStates for Page returns remains the same
         differ[0]
 
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(47 until 59)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1632,7 +1633,7 @@
         // do an invalid prepend which will return LoadResult.Invalid
         differ[0]
         pagingSources[0].nextLoadResult = LoadResult.Invalid()
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceTimeBy(1001)
 
         assertThat(pagingSources.size).isEqualTo(2)
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1645,7 +1646,7 @@
         )
 
         // the LoadResult.Invalid from failed PREPEND triggers new pagingSource + initial REFRESH
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // load starts from 0 again because the provided initialKey = 50 is not multi-generational
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
@@ -1663,7 +1664,7 @@
 
         // execute queued initial REFRESH load which will return LoadResult.Invalid()
         pagingSources[0].nextLoadResult = LoadResult.Invalid()
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceTimeBy(1001)
 
         assertThat(differ.snapshot()).isEmpty()
         assertThat(differ.newCombinedLoadStates()).containsExactly(
@@ -1673,7 +1674,7 @@
         )
 
         // execute second REFRESH load
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // second refresh still loads from initialKey = 50 because anchorPosition/refreshKey is null
         assertThat(pagingSources.size).isEqualTo(2)
@@ -1692,7 +1693,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // initial REFRESH
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(refreshLocal = Loading),
@@ -1705,7 +1706,7 @@
         val exception = Throwable()
         pagingSources[0].nextLoadResult = LoadResult.Error(exception)
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(
@@ -1721,7 +1722,7 @@
 
         // retry append
         differ.retry()
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // make sure append success
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 12)
@@ -1743,7 +1744,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // initial REFRESH
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(refreshLocal = Loading),
@@ -1757,7 +1758,7 @@
         val exception = Throwable()
         pagingSources[0].nextLoadResult = LoadResult.Error(exception)
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(prependLocal = Loading),
@@ -1768,7 +1769,7 @@
         // retry prepend
         differ.retry()
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // make sure prepend success
         assertThat(differ.snapshot()).containsExactlyElementsIn(47 until 59)
@@ -1781,14 +1782,14 @@
     }
 
     @Test
-    fun refreshError_retryLoadStates() = runTest() { differ, loadDispatcher, pagingSources, _, _ ->
+    fun refreshError_retryLoadStates() = runTest { differ, loadDispatcher, pagingSources, _, _ ->
         val collectLoadStates = differ.collectLoadStates()
 
         // initial load returns LoadResult.Error
         val exception = Throwable()
         pagingSources[0].nextLoadResult = LoadResult.Error(exception)
 
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(refreshLocal = Loading),
@@ -1799,7 +1800,7 @@
         // retry refresh
         differ.retry()
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // refresh retry does not trigger new gen
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
@@ -1818,7 +1819,7 @@
         val collectLoadStates = differ.collectLoadStates()
 
         // initial REFRESH
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(refreshLocal = Loading),
@@ -1832,7 +1833,7 @@
         val exception = Throwable()
         pagingSources[0].nextLoadResult = LoadResult.Error(exception)
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(prependLocal = Loading),
@@ -1841,7 +1842,7 @@
 
         // refresh() should reset local LoadStates and trigger new REFRESH
         differ.refresh()
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         // Initial load starts from 0 because initialKey is single gen.
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
@@ -1857,7 +1858,7 @@
     }
 
     @Test
-    fun refreshError_refreshLoadStates() = runTest() { differ, loadDispatcher, pagingSources,
+    fun refreshError_refreshLoadStates() = runTest { differ, loadDispatcher, pagingSources,
         _, _ ->
         val collectLoadStates = differ.collectLoadStates()
 
@@ -1865,7 +1866,7 @@
         val exception = Throwable()
         pagingSources[0].nextLoadResult = LoadResult.Error(exception)
 
-        loadDispatcher.executeAll()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.newCombinedLoadStates()).containsExactly(
             localLoadStatesOf(refreshLocal = Loading),
@@ -1876,7 +1877,7 @@
         // refresh should trigger new generation
         differ.refresh()
 
-        loadDispatcher.queue.poll()?.run()
+        loadDispatcher.scheduler.advanceUntilIdle()
 
         assertThat(differ.snapshot()).containsExactlyElementsIn(0 until 9)
         // Goes directly from Error --> Loading without resetting refresh to NotLoading
@@ -2038,7 +2039,7 @@
     }
 
     private fun runTest(
-        loadDispatcher: TestDispatcher = TestDispatcher(),
+        loadDispatcher: TestDispatcher = StandardTestDispatcher(),
         initialKey: Int? = null,
         pagingSources: MutableList<TestPagingSource> = mutableListOf(),
         pager: Pager<Int, Int> =
@@ -2047,7 +2048,7 @@
                 initialKey = initialKey,
                 pagingSourceFactory = {
                     TestPagingSource(
-                        loadDelay = 0,
+                        loadDelay = 1000,
                         loadContext = loadDispatcher,
                     ).also { pagingSources.add(it) }
                 }
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 599b0b2..e17b5c1 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=9971607
-androidx.playground.metalavaBuildId=9975079
+androidx.playground.metalavaBuildId=10009114
 androidx.studio.type=playground
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
index 8d57a15..95635c5 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
@@ -25,7 +25,7 @@
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -61,7 +61,7 @@
     }
 
     /** @hide */
-    @RestrictTo(RestrictTo.Scope.TESTS)
+    @VisibleForTesting
     @NonNull
     public static PreferenceViewHolder createInstanceForTests(@NonNull View itemView) {
         return new PreferenceViewHolder(itemView);
diff --git a/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
index 6cbc48f..948c1f7 100644
--- a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
+++ b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
@@ -170,9 +170,14 @@
             start(ACTIVITY_NAME)
             evaluateUI {
 
-                // Taimen Api 28 and Cuttlefish Api 29 behave differently.
+                // Taimen Api 28 and Cuttlefish Api 29 behave differently and sometimes return
+                // profile non matching making this test flaky. Here we allow one of those 2
+                // results for these devices.
                 if ((isApi29 && isCuttlefish) || (isApi28 && !isCuttlefish)) {
-                    profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING)
+                    profileInstalled(
+                        RESULT_CODE_COMPILED_WITH_PROFILE,
+                        RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
+                    )
                 } else {
                     profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
                 }
@@ -232,9 +237,14 @@
             start(ACTIVITY_NAME)
             evaluateUI {
 
-                // Taimen Api 28 and Cuttlefish Api 29 behave differently.
+                // Taimen Api 28 and Cuttlefish Api 29 behave differently and sometimes return
+                // profile non matching making this test flaky. Here we allow one of those 2
+                // results for these devices.
                 if ((isApi29 && isCuttlefish) || (isApi28 && !isCuttlefish)) {
-                    profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING)
+                    profileInstalled(
+                        RESULT_CODE_COMPILED_WITH_PROFILE,
+                        RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
+                    )
                 } else {
                     profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
                 }
diff --git a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/TestManager.kt b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/TestManager.kt
index 3e27464..e9fb691e 100644
--- a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/TestManager.kt
+++ b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/TestManager.kt
@@ -273,10 +273,10 @@
     }
 
     class AssertUiBlock(private val lines: List<String>) {
-        fun profileInstalled(resultCode: Int) =
+        fun profileInstalled(vararg resultCodes: Int) =
             assertWithMessage("Unexpected profile verification result code")
                 .that(lines[0].toInt())
-                .isEqualTo(resultCode)
+                .isIn(resultCodes.asIterable())
 
         fun hasReferenceProfile(value: Boolean) =
             assertWithMessage("Unexpected hasReferenceProfile value")
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
index 2669001..8b47bcd 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
@@ -394,6 +394,7 @@
             case Build.VERSION_CODES.S:
             case Build.VERSION_CODES.S_V2:
             case Build.VERSION_CODES.TIRAMISU:
+            case 34:
                 return ProfileVersion.V015_S;
 
             default:
@@ -429,6 +430,7 @@
             case Build.VERSION_CODES.S:
             case Build.VERSION_CODES.S_V2:
             case Build.VERSION_CODES.TIRAMISU:
+            case 34:
                 return true;
 
             default:
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
index 650f8f9..fe44d97 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
@@ -33,7 +33,7 @@
     static final byte[] METADATA_V001_N = new byte[]{'0', '0', '1', '\0'};
     static final byte[] METADATA_V002 = new byte[]{'0', '0', '2', '\0'};
     public static final int MIN_SUPPORTED_SDK = Build.VERSION_CODES.N;
-    public static final int MAX_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
+    public static final int MAX_SUPPORTED_SDK = 34;
 
     static String dexKeySeparator(byte[] version) {
         if (Arrays.equals(version, V001_N)) {
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 6267016..d81c2ef 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -113,8 +113,8 @@
             configuration: "shadowAndImplementation")
 
     androidTestImplementation(project(":annotation:annotation-experimental"))
-    androidTestImplementation(project(":arch:core:core-runtime"))
-    androidTestImplementation(project(":arch:core:core-common"))
+    androidTestImplementation(projectOrArtifact(":arch:core:core-runtime"))
+    androidTestImplementation(projectOrArtifact(":arch:core:core-common"))
     androidTestImplementation(project(":room:room-testing"))
     androidTestImplementation(project(":room:room-rxjava2"))
     androidTestImplementation(project(":room:room-rxjava3"))
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
index 5cebcec..fc964f9 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
@@ -16,17 +16,19 @@
 
 package androidx.room.compiler.processing.util
 
+import com.squareup.kotlinpoet.TypeSpec as KTypeSpec
 import androidx.room.compiler.processing.ExperimentalProcessingApi
+import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.compat.XConverters.toXProcessing
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
 import com.squareup.kotlinpoet.BOOLEAN
 import com.squareup.kotlinpoet.FileSpec
-import com.squareup.kotlinpoet.TypeSpec as KTypeSpec
 import java.io.File
-import org.junit.Test
 import org.junit.AssumptionViolatedException
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
@@ -59,6 +61,38 @@
     }
 
     @Test
+    fun successfulGeneratedJavaCodeMatchWithWriteSource() {
+        val file = JavaFile.builder(
+            "foo.bar",
+            TypeSpec.classBuilder("Baz").build()
+        ).build()
+        runTest { invocation ->
+            if (invocation.processingEnv.findTypeElement("foo.bar.Baz") == null) {
+                val originatingElements: List<XElement> =
+                    file.typeSpec.originatingElements.map {
+                        it.toXProcessing(invocation.processingEnv)
+                    }
+                invocation.processingEnv.filer.writeSource(
+                    file.packageName,
+                    file.typeSpec.name,
+                    "java",
+                    originatingElements
+                ).bufferedWriter().use {
+                    it.write(file.toString())
+                }
+            }
+            invocation.assertCompilationResult {
+                generatedSource(
+                    Source.java(
+                        "foo.bar.Baz",
+                        file.toString()
+                    )
+                )
+            }
+        }
+    }
+
+    @Test
     fun missingGeneratedCode() {
         val result = runCatching {
             runTest { invocation ->
@@ -120,6 +154,38 @@
     }
 
     @Test
+    fun successfulGeneratedKotlinCodeMatchWithWriteSource() {
+        // java environment will not generate kotlin files
+        runTest.assumeCanCompileKotlin()
+
+        val type = KTypeSpec.classBuilder("Baz").build()
+        val file = FileSpec.builder("foo.bar", "Baz")
+            .addType(type)
+            .build()
+        runTest { invocation ->
+            if (invocation.processingEnv.findTypeElement("foo.bar.Baz") == null) {
+                val originatingElements: List<XElement> =
+                    type.originatingElements.map {
+                        it.toXProcessing(invocation.processingEnv)
+                    }
+                invocation.processingEnv.filer.writeSource(
+                    file.packageName,
+                    file.name,
+                    "kt",
+                    originatingElements
+                ).bufferedWriter().use {
+                    it.write(file.toString())
+                }
+            }
+            invocation.assertCompilationResult {
+                generatedSource(
+                    Source.kotlin(combine("foo", "bar", "Baz.kt"), file.toString())
+                )
+            }
+        }
+    }
+
+    @Test
     fun successfulGeneratedKotlinCodeMatch() {
         // java environment will not generate kotlin files
         runTest.assumeCanCompileKotlin()
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt
index 533b018..46bfe08 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt
@@ -35,6 +35,22 @@
     fun write(fileSpec: FileSpec, mode: Mode = Mode.Isolating)
 
     /**
+     * Writes a source file that will be part of the output artifact (e.g. jar).
+     *
+     * Only source files should be written via this function, if the extension is not `.java` or
+     * `.kt` this function will throw an exception.
+     *
+     * @return the output stream to write the resource file.
+     */
+    fun writeSource(
+        packageName: String,
+        fileNameWithoutExtension: String,
+        extension: String,
+        originatingElements: List<XElement>,
+        mode: Mode = Mode.Isolating
+    ): OutputStream
+
+    /**
      * Writes a resource file that will be part of the output artifact (e.g. jar).
      *
      * Only non-source files should be written via this function, if the file path corresponds to a
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
index 1874984..2e37934 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -56,6 +56,7 @@
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticContinuationParameterElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticReceiverParameterElement
+import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.symbol.KSAnnotated
 import com.google.devtools.ksp.symbol.KSAnnotation
@@ -173,6 +174,9 @@
     fun XProcessingEnv.toKS(): SymbolProcessorEnvironment = (this as KspProcessingEnv).delegate
 
     @JvmStatic
+    fun XProcessingEnv.toKSResolver(): Resolver = (this as KspProcessingEnv).resolver
+
+    @JvmStatic
     fun XTypeElement.toKS(): KSClassDeclaration = (this as KspTypeElement).declaration
 
     @JvmStatic
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt
index 93578c7..2043d41 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt
@@ -46,6 +46,37 @@
         fileSpec.writeTo(delegate)
     }
 
+    override fun writeSource(
+        packageName: String,
+        fileNameWithoutExtension: String,
+        extension: String,
+        originatingElements: List<XElement>,
+        mode: XFiler.Mode
+    ): OutputStream {
+        require(extension == "java" || extension == "kt") {
+            "Source file extension must be either 'java' or 'kt', but was: $extension"
+        }
+        val javaOriginatingElements =
+            originatingElements.filterIsInstance<JavacElement>().map { it.element }.toTypedArray()
+        return when (extension) {
+            "java" -> {
+                delegate.createSourceFile(
+                    "$packageName.$fileNameWithoutExtension",
+                    *javaOriginatingElements
+                ).openOutputStream()
+            }
+            "kt" -> {
+                delegate.createResource(
+                    StandardLocation.SOURCE_OUTPUT,
+                    packageName,
+                    "$fileNameWithoutExtension.$extension",
+                    *javaOriginatingElements
+                ).openOutputStream()
+            }
+            else -> error("file type not supported: $extension")
+        }
+    }
+
     override fun writeResource(
         filePath: Path,
         originatingElements: List<XElement>,
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 abada70..bdade61 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
@@ -166,10 +166,17 @@
                 }
             } else {
                 // For method parameters and var type scopes, we don't use the declaration-site
-                // variance if the last variance in the declaration-site stack was invariant and
-                // the last variance in the use-site stack was not contravariant.
-                if (typeParamStack.lastOrNull()?.variance == Variance.INVARIANT &&
-                    typeArgStack.lastOrNull()?.variance != Variance.CONTRAVARIANT) {
+                // variance if all of the following conditions apply.
+                if ( // If the last variance in the type argument stack is not contravariant
+                    typeArgStack.isNotEmpty() &&
+                    typeArgStack.last().variance != Variance.CONTRAVARIANT &&
+                    // And the type parameter stack contains at least one invariant parameter.
+                    typeParamStack.isNotEmpty() &&
+                    typeParamStack.any { it.variance == Variance.INVARIANT } &&
+                    // And the first invariant comes before the last contravariant (if any).
+                    typeParamStack.indexOfFirst { it.variance == Variance.INVARIANT } >=
+                    typeParamStack.indexOfLast { it.variance == Variance.CONTRAVARIANT }
+                ) {
                     return false
                 }
             }
@@ -218,6 +225,7 @@
         declarationType: KSType? = null,
         scope: KSTypeVarianceResolverScope,
         typeStack: ReferenceStack = ReferenceStack(),
+        typeParamStack: List<KSTypeParameter> = emptyList(),
     ): KSType {
         if (type.isError || typeStack.queue.contains(type)) {
             return type
@@ -237,9 +245,10 @@
                     type.arguments.indices.map { i ->
                         getJavaWildcardWithTypeVariablesForInnerType(
                             typeArg = type.arguments[i],
-                            declarationTypeParameter = type.declaration.typeParameters[i],
+                            typeParam = type.declaration.typeParameters[i],
                             scope = scope,
                             typeStack = typeStack,
+                            typeParamStack = typeParamStack
                         )
                     }
                 }
@@ -249,9 +258,10 @@
 
     private fun getJavaWildcardWithTypeVariablesForInnerType(
         typeArg: KSTypeArgument,
-        declarationTypeParameter: KSTypeParameter,
+        typeParam: KSTypeParameter,
         scope: KSTypeVarianceResolverScope,
         typeStack: ReferenceStack,
+        typeParamStack: List<KSTypeParameter>,
     ): KSTypeArgument {
         val type = typeArg.type?.resolve()
         if (
@@ -266,9 +276,20 @@
             type = type,
             scope = scope,
             typeStack = typeStack,
+            typeParamStack = typeParamStack + typeParam,
         )
-        val resolvedVariance = if (declarationTypeParameter.variance != Variance.INVARIANT) {
-            declarationTypeParameter.variance
+        val resolvedVariance = if (
+            typeParam.variance != Variance.INVARIANT &&
+            // This is a weird rule, but empirically whether or not we inherit type variance in
+            // this case depends on the scope of the type used when calling asMemberOf. For
+            // example, if XMethodElement#asMemberOf(XType) is called with an XType that has no
+            // scope or has a matching method scope then we inherit the parameter variance; however,
+            // if asMemberOf was called with an XType that was from a different scope we only
+            // inherit variance here if there is at least one contravariant in the param stack.
+            (scope.asMemberOfScopeOrSelf() == scope ||
+                typeParamStack.any { it.variance == Variance.CONTRAVARIANT })
+        ) {
+            typeParam.variance
         } else {
             typeArg.variance
         }
@@ -296,8 +317,7 @@
             scope = scope,
             typeStack = typeStack
         )
-        val resolvedVariance = if (declarationTypeArg.variance != Variance.INVARIANT &&
-            (!scope.isValOrReturnType() || declarationTypeArg.variance != Variance.COVARIANT)) {
+        val resolvedVariance = if (declarationTypeArg.variance != Variance.INVARIANT) {
             declarationTypeArg.variance
         } else {
             typeArg.variance
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 9b4c646..f39ab11 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
@@ -28,7 +28,8 @@
  */
 internal sealed class KSTypeVarianceResolverScope(
     private val annotated: KSAnnotated,
-    private val container: KSDeclaration?
+    private val container: KSDeclaration?,
+    private val asMemberOf: KspType?
 ) {
     /**
      * Checks whether we need wildcard resolution at all. It is only necessary if either the method
@@ -58,12 +59,18 @@
     /** Returns `true` if this scope represents a val property or a return type. */
     abstract fun isValOrReturnType(): Boolean
 
+    /** Returns the scope of the `asMemberOf` container type. */
+    fun asMemberOfScopeOrSelf(): KSTypeVarianceResolverScope? {
+        return asMemberOf?.scope ?: this
+    }
+
     internal class MethodParameter(
         private val kspExecutableElement: KspExecutableElement,
         private val parameterIndex: Int,
         annotated: KSAnnotated,
         container: KSDeclaration?,
-    ) : KSTypeVarianceResolverScope(annotated, container) {
+        asMemberOf: KspType?,
+    ) : KSTypeVarianceResolverScope(annotated, container, asMemberOf) {
         override fun declarationType() =
             (kspExecutableElement.parameters[parameterIndex].type as KspType).ksType
 
@@ -71,10 +78,12 @@
     }
 
     internal class PropertySetterParameterType(
-        private val setterMethod: KspSyntheticPropertyMethodElement.Setter
+        private val setterMethod: KspSyntheticPropertyMethodElement.Setter,
+        asMemberOf: KspType?,
     ) : KSTypeVarianceResolverScope(
         annotated = setterMethod.accessor,
-        container = setterMethod.field.enclosingElement.declaration
+        container = setterMethod.field.enclosingElement.declaration,
+        asMemberOf = asMemberOf,
     ) {
         override fun declarationType(): KSType {
             // We return the declaration from the setter, not the field because the setter parameter
@@ -86,10 +95,12 @@
     }
 
     internal class PropertyGetterMethodReturnType(
-        private val getterMethod: KspSyntheticPropertyMethodElement.Getter
+        private val getterMethod: KspSyntheticPropertyMethodElement.Getter,
+        asMemberOf: KspType?,
     ) : KSTypeVarianceResolverScope(
         annotated = getterMethod.accessor,
-        container = getterMethod.field.enclosingElement.declaration
+        container = getterMethod.field.enclosingElement.declaration,
+        asMemberOf = asMemberOf,
     ) {
         override fun declarationType(): KSType {
             // We return the declaration from the getter, not the field because the getter return
@@ -100,18 +111,26 @@
         override fun isValOrReturnType() = true
     }
 
-    internal class PropertyType(val field: KspFieldElement) : KSTypeVarianceResolverScope(
+    internal class PropertyType(
+        val field: KspFieldElement,
+        asMemberOf: KspType?,
+    ) : KSTypeVarianceResolverScope(
         annotated = field.declaration,
-        container = field.enclosingElement.declaration
+        container = field.enclosingElement.declaration,
+        asMemberOf = asMemberOf,
     ) {
         override fun declarationType() = field.type.ksType
 
         override fun isValOrReturnType() = field.isFinal()
     }
 
-    internal class MethodReturnType(val method: KspMethodElement) : KSTypeVarianceResolverScope(
+    internal class MethodReturnType(
+        val method: KspMethodElement,
+        asMemberOf: KspType?,
+    ) : KSTypeVarianceResolverScope(
         annotated = method.declaration,
-        container = method.enclosingElement.declaration
+        container = method.enclosingElement.declaration,
+        asMemberOf = asMemberOf,
     ) {
         override fun declarationType() = method.returnType.ksType
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
index 84cf572..a840aa4 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
@@ -24,7 +24,6 @@
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertySetter
-import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.KSValueParameter
 
 internal class KspExecutableParameterElement(
@@ -48,7 +47,7 @@
         get() = parameter.hasDefault
 
     override val type: KspType by lazy {
-        asMemberOf(enclosingElement.enclosingElement.type?.ksType)
+        createAsMemberOf(closestMemberContainer.type)
     }
 
     override val closestMemberContainer: XMemberContainer by lazy {
@@ -59,26 +58,28 @@
         get() = "$name in ${enclosingElement.fallbackLocationText}"
 
     override fun asMemberOf(other: XType): KspType {
-        if (closestMemberContainer.type?.isSameType(other) != false) {
-            return type
+        return if (closestMemberContainer.type?.isSameType(other) != false) {
+            type
+        } else {
+            createAsMemberOf(other)
         }
-        check(other is KspType)
-        return asMemberOf(other.ksType)
     }
 
-    private fun asMemberOf(ksType: KSType?): KspType {
+    private fun createAsMemberOf(container: XType?): KspType {
+        check(container is KspType?)
         return env.wrap(
             originatingReference = parameter.type,
             ksType = parameter.typeAsMemberOf(
                 functionDeclaration = enclosingElement.declaration,
-                ksType = ksType
+                ksType = container?.ksType
             )
         ).copyWithScope(
             KSTypeVarianceResolverScope.MethodParameter(
                 kspExecutableElement = enclosingElement,
                 parameterIndex = parameterIndex,
                 annotated = parameter.type,
-                container = ksType?.declaration
+                container = container?.ksType?.declaration,
+                asMemberOf = container,
             )
         )
     }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
index b8bad187..beab250 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
@@ -24,7 +24,6 @@
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import com.google.devtools.ksp.isPrivate
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
-import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.Modifier
 
 internal class KspFieldElement(
@@ -48,7 +47,7 @@
     }
 
     override val type: KspType by lazy {
-        asMemberOf(enclosingElement.type?.ksType)
+        createAsMemberOf(closestMemberContainer.type)
     }
 
     override val jvmDescriptor: String
@@ -92,18 +91,24 @@
         }
 
     override fun asMemberOf(other: XType): KspType {
-        if (enclosingElement.type?.isSameType(other) != false) {
-            return type
+        return if (closestMemberContainer.type?.isSameType(other) != false) {
+            type
+        } else {
+            return createAsMemberOf(other)
         }
-        check(other is KspType)
-        return asMemberOf(other.ksType)
     }
 
-    private fun asMemberOf(ksType: KSType?): KspType {
+    private fun createAsMemberOf(container: XType?): KspType {
+        check(container is KspType?)
         return env.wrap(
             originatingReference = declaration.type,
-            ksType = declaration.typeAsMemberOf(ksType)
-        ).copyWithScope(KSTypeVarianceResolverScope.PropertyType(this))
+            ksType = declaration.typeAsMemberOf(container?.ksType)
+        ).copyWithScope(
+            KSTypeVarianceResolverScope.PropertyType(
+                field = this,
+                asMemberOf = container,
+            )
+        )
     }
 
     companion object {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
index 7c92ac3..0c04d79 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
@@ -75,6 +75,28 @@
         }
     }
 
+    override fun writeSource(
+        packageName: String,
+        fileNameWithoutExtension: String,
+        extension: String,
+        originatingElements: List<XElement>,
+        mode: XFiler.Mode
+    ): OutputStream {
+        require(extension == "java" || extension == "kt") {
+            "Source file extension must be either 'java' or 'kt', but was: $extension"
+        }
+        val kspFilerOriginatingElements = originatingElements
+            .mapNotNull { it.originatingElementForPoet() }
+            .toOriginatingElements()
+        return createNewFile(
+            originatingElements = kspFilerOriginatingElements,
+            packageName = packageName,
+            fileName = fileNameWithoutExtension,
+            extensionName = extension,
+            aggregating = mode == XFiler.Mode.Aggregating
+        )
+    }
+
     override fun writeResource(
         filePath: Path,
         originatingElements: List<XElement>,
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
index 131b645..841154a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
@@ -122,7 +122,12 @@
             declaration.returnKspType(
                 env = env,
                 containing = enclosingElement.type
-            ).copyWithScope(KSTypeVarianceResolverScope.MethodReturnType(this))
+            ).copyWithScope(
+                KSTypeVarianceResolverScope.MethodReturnType(
+                    method = this,
+                    asMemberOf = enclosingElement.type,
+                )
+            )
         }
         override fun isSuspendFunction() = false
     }
@@ -137,7 +142,12 @@
             env.wrap(
                 ksType = env.resolver.builtIns.anyType.makeNullable(),
                 allowPrimitives = false
-            ).copyWithScope(KSTypeVarianceResolverScope.MethodReturnType(this))
+            ).copyWithScope(
+                KSTypeVarianceResolverScope.MethodReturnType(
+                    method = this,
+                    asMemberOf = enclosingElement.type
+                )
+            )
         }
 
         override val parameters: List<XExecutableParameterElement>
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
index fba2cbc..600bb01 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
@@ -47,7 +47,12 @@
             origin.declaration.returnKspType(
                 env = env,
                 containing = containing
-            ).copyWithScope(KSTypeVarianceResolverScope.MethodReturnType(origin))
+            ).copyWithScope(
+                KSTypeVarianceResolverScope.MethodReturnType(
+                    method = origin,
+                    asMemberOf = containing
+                )
+            )
         }
     }
 
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 8fc4f16..74709b2 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
@@ -52,7 +52,7 @@
     /**
      * Type resolver to convert KSType into its JVM representation.
      */
-    protected val scope: KSTypeVarianceResolverScope?
+    val scope: KSTypeVarianceResolverScope?
 ) : KspAnnotated(env), XType, XEquality {
     override val rawType by lazy {
         KspRawType(this)
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
index 1fbc669..de2c250 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
@@ -30,7 +30,6 @@
 import androidx.room.compiler.processing.ksp.requireContinuationClass
 import androidx.room.compiler.processing.ksp.returnTypeAsMemberOf
 import androidx.room.compiler.processing.ksp.swapResolvedType
-import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.Variance
 
 /**
@@ -75,7 +74,7 @@
         get() = false
 
     override val type: KspType by lazy {
-        asMemberOf(enclosingElement.enclosingElement.type?.ksType)
+        createAsMemberOf(closestMemberContainer.type)
     }
 
     override val fallbackLocationText: String
@@ -89,17 +88,18 @@
     }
 
     override fun asMemberOf(other: XType): KspType {
-        if (enclosingElement.enclosingElement.type?.isSameType(other) != false) {
-            return type
+        return if (closestMemberContainer.type?.isSameType(other) != false) {
+            type
+        } else {
+            createAsMemberOf(other)
         }
-        check(other is KspType)
-        return asMemberOf(other.ksType)
     }
 
-    private fun asMemberOf(ksType: KSType?): KspType {
+    private fun createAsMemberOf(container: XType?): KspType {
+        check(container is KspType?)
         val continuation = env.resolver.requireContinuationClass()
         val asMember = enclosingElement.declaration.returnTypeAsMemberOf(
-            ksType = ksType
+            ksType = container?.ksType
         )
         val returnTypeRef = checkNotNull(enclosingElement.declaration.returnType) {
             "cannot find return type reference for $this"
@@ -119,7 +119,8 @@
                 kspExecutableElement = enclosingElement,
                 parameterIndex = enclosingElement.parameters.size - 1,
                 annotated = enclosingElement.declaration,
-                container = ksType?.declaration
+                container = container?.ksType?.declaration,
+                asMemberOf = container
             )
         )
     }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index b5e1a70..2067b96 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -34,6 +34,7 @@
 import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE_OR_SET_PARAM
 import androidx.room.compiler.processing.ksp.KspFieldElement
 import androidx.room.compiler.processing.ksp.KspHasModifiers
+import androidx.room.compiler.processing.ksp.KspMemberContainer
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.ksp.KspType
 import androidx.room.compiler.processing.ksp.findEnclosingMemberContainer
@@ -84,7 +85,7 @@
 
     final override fun isExtensionFunction() = false
 
-    final override val enclosingElement: XMemberContainer
+    final override val enclosingElement: KspMemberContainer
         get() = this.field.enclosingElement
 
     final override val closestMemberContainer: XMemberContainer by lazy {
@@ -119,6 +120,7 @@
     }
 
     final override fun asMemberOf(other: XType): XMethodType {
+        check(other is KspType)
         return KspSyntheticPropertyMethodType.create(
             env = env,
             element = this,
@@ -168,7 +170,10 @@
 
         override val returnType: XType by lazy {
             field.type.copyWithScope(
-                KSTypeVarianceResolverScope.PropertyGetterMethodReturnType(this)
+                KSTypeVarianceResolverScope.PropertyGetterMethodReturnType(
+                    getterMethod = this,
+                    asMemberOf = enclosingElement.type,
+                )
             )
         }
 
@@ -247,7 +252,10 @@
 
             override val type: KspType by lazy {
                 enclosingElement.field.type.copyWithScope(
-                    KSTypeVarianceResolverScope.PropertySetterParameterType(enclosingElement)
+                    KSTypeVarianceResolverScope.PropertySetterParameterType(
+                        setterMethod = enclosingElement,
+                        asMemberOf = enclosingElement.enclosingElement.type,
+                    )
                 )
             }
 
@@ -262,9 +270,16 @@
             }
 
             override fun asMemberOf(other: XType): KspType {
+                if (closestMemberContainer.type?.isSameType(other) != false) {
+                    return type
+                }
+                check(other is KspType)
                 return enclosingElement.field.asMemberOf(other)
                     .copyWithScope(
-                        KSTypeVarianceResolverScope.PropertySetterParameterType(enclosingElement)
+                        KSTypeVarianceResolverScope.PropertySetterParameterType(
+                            setterMethod = enclosingElement,
+                            asMemberOf = other,
+                        )
                     )
             }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodType.kt
index a482093..d1bd312 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodType.kt
@@ -21,6 +21,7 @@
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.ksp.KSTypeVarianceResolverScope
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
+import androidx.room.compiler.processing.ksp.KspType
 import com.google.devtools.ksp.symbol.KSPropertyGetter
 import com.google.devtools.ksp.symbol.KSPropertySetter
 import com.squareup.javapoet.TypeVariableName
@@ -61,7 +62,7 @@
         fun create(
             env: KspProcessingEnv,
             element: KspSyntheticPropertyMethodElement,
-            container: XType?
+            container: KspType?
         ): XMethodType {
             return when (element.accessor) {
                 is KSPropertyGetter ->
@@ -84,7 +85,7 @@
     private class Getter(
         env: KspProcessingEnv,
         origin: KspSyntheticPropertyMethodElement,
-        containingType: XType?
+        containingType: KspType?
     ) : KspSyntheticPropertyMethodType(
         env = env,
         origin = origin,
@@ -97,7 +98,8 @@
                 origin.field.asMemberOf(containingType)
             }.copyWithScope(
                 KSTypeVarianceResolverScope.PropertyGetterMethodReturnType(
-                    getterMethod = origin as KspSyntheticPropertyMethodElement.Getter
+                    getterMethod = origin as KspSyntheticPropertyMethodElement.Getter,
+                    asMemberOf = containingType
                 )
             )
         }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
index 94224fe..0ebf74c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
@@ -26,7 +26,6 @@
 import androidx.room.compiler.processing.ksp.KspMethodElement
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.ksp.KspType
-import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.KSTypeReference
 
 internal class KspSyntheticReceiverParameterElement(
@@ -60,7 +59,7 @@
         get() = false
 
     override val type: KspType by lazy {
-        asMemberOf(enclosingElement.enclosingElement.type?.ksType)
+        createAsMemberOf(closestMemberContainer.type)
     }
 
     override val fallbackLocationText: String
@@ -74,19 +73,20 @@
     }
 
     override fun asMemberOf(other: XType): KspType {
-        if (closestMemberContainer.type?.isSameType(other) != false) {
-            return type
+        return if (closestMemberContainer.type?.isSameType(other) != false) {
+            type
+        } else {
+            createAsMemberOf(other)
         }
-        check(other is KspType)
-        return asMemberOf(other.ksType)
     }
 
-    private fun asMemberOf(ksType: KSType?): KspType {
+    private fun createAsMemberOf(container: XType?): KspType {
+        check(container is KspType?)
         val asMemberReceiverType = receiverType.resolve().let {
-            if (ksType == null || it.isError) {
+            if (container?.ksType == null || it.isError) {
                 return@let it
             }
-            val asMember = enclosingElement.declaration.asMemberOf(ksType)
+            val asMember = enclosingElement.declaration.asMemberOf(container?.ksType)
             checkNotNull(asMember.extensionReceiverType)
         }
         return env.wrap(
@@ -97,7 +97,8 @@
                 kspExecutableElement = enclosingElement,
                 parameterIndex = 0, // Receiver param is the 1st one
                 annotated = enclosingElement.declaration,
-                container = ksType?.declaration
+                container = container?.ksType?.declaration,
+                asMemberOf = container
             )
         )
     }
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
new file mode 100644
index 0000000..16b8f8c
--- /dev/null
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.room.compiler.processing.ksp
+
+import androidx.room.compiler.processing.XMethodType
+import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
+import androidx.room.compiler.processing.util.runKaptTest
+import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class KSTypeVarianceResolverWithTypeParametersTest(
+    private val t0: String,
+    private val t1: String,
+    private val t2: String,
+    private val t3: String,
+    private val t4: String,
+) {
+    @Test
+    fun testResults() {
+        // Note: The compilation results for all parameters of this parameterized test are
+        // calculated at the same time (on the first call to compilationResults) because its much
+        // faster to compile the sources all together rather than once per parameter. However,
+        // there's still benefit to keeping this test parameterized since it makes it easier to
+        // parse breakages due to a particular parameter.
+        val key = key(t0, t1, t2, t3, t4)
+        assertThat(compilationResults.kspSignaturesMap[key])
+            .containsExactlyElementsIn(compilationResults.kaptSignaturesMap[key])
+            .inOrder()
+    }
+
+    private companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}-{1}-{2}-{3}-{4}")
+        fun params(): List<Array<String>> {
+            val genericTypes = listOf("Foo", "FooIn", "FooOut")
+            val parameters: MutableList<Array<String>> = mutableListOf()
+            for (t0 in genericTypes) {
+                for (t1 in genericTypes) {
+                    for (t2 in genericTypes) {
+                        for (t3 in genericTypes) {
+                            for (t4 in genericTypes) {
+                                parameters.add(arrayOf(t0, t1, t2, t3, t4))
+                            }
+                        }
+                    }
+                }
+            }
+            return parameters
+        }
+
+        val compilationResults: CompilationResults by lazy {
+            val sourcesMap = params().associate { (t0, t1, t2, t3, t4) ->
+                val key = key(t0, t1, t2, t3, t4)
+                key to listOf(
+                    source("${key}0", "T", t = "$t0<$t1<$t2<$t3<$t4<Bar>>>>>"),
+                    source("${key}1", "$t0<T>", t = "$t1<$t2<$t3<$t4<Bar>>>>"),
+                    source("${key}2", "$t0<$t1<T>>", t = "$t2<$t3<$t4<Bar>>>"),
+                    source("${key}3", "$t0<$t1<$t2<T>>>", t = "$t3<$t4<Bar>>"),
+                    source("${key}4", "$t0<$t1<$t2<$t3<T>>>>", t = "$t4<Bar>"),
+                    source("${key}5", "$t0<$t1<$t2<$t3<$t4<T>>>>>", t = "Bar"),
+                )
+            }
+            val sources = sourcesMap.values.flatten() + Source.kotlin(
+                "SharedInterfaces.kt",
+                """
+                interface Foo<T>
+                interface FooIn<in T>
+                interface FooOut<out T>
+                interface Bar
+                """.trimIndent()
+            )
+            val kaptSignaturesMap = buildMap(sourcesMap.size) {
+                runKaptTest(sources) {
+                    sourcesMap.keys.forEach { key ->
+                        put(key, IntRange(0, 5).flatMap { i -> collectSignatures(it, key, i) })
+                    }
+                }
+            }
+            val kspSignaturesMap = buildMap(sourcesMap.size) {
+                runKspTest(sources) {
+                    sourcesMap.keys.forEach { key ->
+                        put(key, IntRange(0, 5).flatMap { i -> collectSignatures(it, key, i) })
+                    }
+                }
+            }
+            CompilationResults(kaptSignaturesMap, kspSignaturesMap)
+        }
+
+        private fun source(suffix: String, type: String, t: String): Source {
+            val sub = "Sub$suffix"
+            val base = "Base$suffix"
+            return Source.kotlin(
+                "$sub.kt",
+                """
+                class $sub: $base<$t>() {
+                    fun subMethod(param: $base<$t>): $base<$t> = TODO()
+                }
+                open class $base<T> {
+                    fun baseMethod(param: $type): $type = TODO()
+                }
+                """.trimIndent()
+            )
+        }
+
+        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.
+            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"
+                    }
+                    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()}")
+                }
+            }
+        }
+
+        fun key(t0: String, t1: String, t2: String, t3: String, t4: String) = "$t0$t1$t2$t3$t4"
+
+        data class CompilationResults(
+            val kaptSignaturesMap: Map<String, List<String>>,
+            val kspSignaturesMap: Map<String, List<String>>,
+        )
+    }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 1a50a33..50f046f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -202,11 +202,6 @@
 }
 
 private String getRequestedProjectSubsetName() {
-    def prop = providers.gradleProperty("androidx.projects")
-    if (prop.isPresent()) {
-        return prop.get().toUpperCase()
-    }
-
     def envProp = providers.environmentVariable("ANDROIDX_PROJECTS")
     if (envProp.isPresent()) {
         return envProp.get().toUpperCase()
@@ -214,8 +209,15 @@
     return null
 }
 
+private String getRequestedProjectPrefix() {
+    def envProp = providers.environmentVariable("PROJECT_PREFIX")
+    if (envProp.isPresent()) {
+        return envProp.get()
+    }
+    return null
+}
+
 boolean isAllProjects() {
-    String projectSubsetName = getRequestedProjectSubsetName()
     return requestedProjectSubsetName == null || requestedProjectSubsetName == "ALL"
 }
 
@@ -313,10 +315,12 @@
 @Field Map<String, File> allProjects = new HashMap<String, File>()
 // A set of projects that the user asked to filter to.
 @Field Set<String> filteredProjects = new HashSet<String>()
+filteredProjects.add(":lint-checks")
 
 @Field Pattern projectReferencePattern = Pattern.compile(
         "(project|projectOrArtifact)\\((path: )?[\"'](?<name>\\S*)[\"'](, configuration: .*)?\\)"
 )
+@Field Pattern multilineProjectReference = Pattern.compile("project\\(\$")
 @Field Pattern inspection = Pattern.compile("packageInspector\\(project, \"(.*)\"\\)")
 @Field Pattern composePlugin = Pattern.compile("id\\(\"AndroidXComposePlugin\"\\)")
 @Field Pattern paparazziPlugin = Pattern.compile("id\\(\"AndroidXPaparazziPlugin\"\\)")
@@ -331,8 +335,10 @@
 //   the project name in the IDE
 //   the Maven artifactId
 //
-def includeProject(name, filePath, List<BuildType> filter = []) {
-    if (shouldIncludeForFilter(filter)) filteredProjects.add(name)
+def includeProject(String name, filePath, List<BuildType> filter = []) {
+    if (getRequestedProjectPrefix() != null) {
+        if (name.startsWith(getRequestedProjectPrefix())) filteredProjects.add(name)
+    } else if (shouldIncludeForFilter(filter)) filteredProjects.add(name)
     def file
     if (filePath != null) {
         if (filePath instanceof String) {
@@ -349,12 +355,19 @@
     allProjects[name] = file
     File buildGradle = new File(file, "build.gradle")
     if (buildGradle.exists()) {
+        def buildGradleProperty = settings.services.get(ObjectFactory).fileProperty().fileValue(buildGradle)
+        def contents = settings.providers.fileContents(buildGradleProperty).getAsText().get()
         Set<String> links = new HashSet<String>()
-        for (line in buildGradle.readLines()) {
+        for (line in contents.lines()) {
             Matcher m = projectReferencePattern.matcher(line)
             if (m.find()) {
                 links.add(m.group("name"))
             }
+            if (multilineProjectReference.matcher(line).find()) {
+                throw new IllegalStateException(
+                        "Multi-line project() references are not supported. Please fix $file.absolutePath"
+                )
+            }
             Matcher matcherInspection = inspection.matcher(line)
             if (matcherInspection) {
                 links.add(matcherInspection.group(1))
@@ -426,8 +439,8 @@
 includeProject(":appsearch:appsearch-local-storage", [BuildType.MAIN])
 includeProject(":appsearch:appsearch-platform-storage", [BuildType.MAIN])
 includeProject(":appsearch:appsearch-test-util", [BuildType.MAIN])
-includeProject(":arch:core:core-common", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":arch:core:core-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":arch:core:core-common", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":arch:core:core-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":arch:core:core-testing", [BuildType.MAIN])
 includeProject(":asynclayoutinflater:asynclayoutinflater", [BuildType.MAIN])
 includeProject(":asynclayoutinflater:asynclayoutinflater-appcompat", [BuildType.MAIN])
@@ -524,9 +537,9 @@
 includeProject(":compose:animation:animation-graphics:animation-graphics-samples", "compose/animation/animation-graphics/samples", [BuildType.COMPOSE])
 includeProject(":compose:benchmark-utils", [BuildType.COMPOSE])
 includeProject(":compose:benchmark-utils:benchmark-utils-benchmark", "compose/benchmark-utils/benchmark", [BuildType.COMPOSE])
-includeProject(":compose:compiler:compiler", [BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":compose:compiler:compiler", [BuildType.COMPOSE])
 includeProject(":compose:compiler:compiler:integration-tests", [BuildType.COMPOSE])
-includeProject(":compose:compiler:compiler-hosted", [BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":compose:compiler:compiler-hosted", [BuildType.COMPOSE])
 includeProject(":compose:compiler:compiler-hosted:integration-tests", [BuildType.COMPOSE])
 includeProject(":compose:compiler:compiler-hosted:integration-tests:kotlin-compiler-repackaged", [BuildType.COMPOSE])
 includeProject(":compose:compiler:compiler-daemon", [BuildType.COMPOSE])
@@ -554,10 +567,10 @@
 includeProject(":compose:integration-tests:macrobenchmark", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:macrobenchmark-target", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:material-catalog", [BuildType.COMPOSE])
-includeProject(":compose:lint", [BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":compose:lint:internal-lint-checks", [BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":compose:lint:common", [BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":compose:lint:common-test", [BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":compose:lint", [BuildType.COMPOSE])
+includeProject(":compose:lint:internal-lint-checks", [BuildType.COMPOSE])
+includeProject(":compose:lint:common", [BuildType.COMPOSE])
+includeProject(":compose:lint:common-test", [BuildType.COMPOSE])
 includeProject(":compose:material", [BuildType.COMPOSE])
 includeProject(":compose:material3:material3", [BuildType.COMPOSE])
 includeProject(":compose:material3:benchmark", [BuildType.COMPOSE])
@@ -584,9 +597,9 @@
 includeProject(":compose:material3:material3:integration-tests:material3-catalog", [BuildType.COMPOSE])
 includeProject(":compose:material:material:material-samples", "compose/material/material/samples", [BuildType.COMPOSE])
 includeProject(":compose:material3:material3:material3-samples", "compose/material3/material3/samples", [BuildType.COMPOSE])
-includeProject(":compose:runtime", [BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":compose:runtime:runtime", [BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":compose:runtime:runtime-lint", [BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":compose:runtime", [BuildType.COMPOSE])
+includeProject(":compose:runtime:runtime", [BuildType.COMPOSE])
+includeProject(":compose:runtime:runtime-lint", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-livedata", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-livedata:runtime-livedata-samples", "compose/runtime/runtime-livedata/samples", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-tracing", [BuildType.COMPOSE])
@@ -599,7 +612,7 @@
 includeProject(":compose:runtime:runtime-saveable:runtime-saveable-samples", "compose/runtime/runtime-saveable/samples", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime:benchmark", "compose/runtime/runtime/compose-runtime-benchmark", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime:integration-tests", [BuildType.COMPOSE])
-includeProject(":compose:runtime:runtime:runtime-samples", "compose/runtime/runtime/samples", [BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":compose:runtime:runtime:runtime-samples", "compose/runtime/runtime/samples", [BuildType.COMPOSE])
 includeProject(":compose:test-utils", [BuildType.COMPOSE])
 includeProject(":compose:ui", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui", [BuildType.COMPOSE])
@@ -633,8 +646,8 @@
 includeProject(":compose:ui:ui-viewbinding:ui-viewbinding-samples", "compose/ui/ui-viewbinding/samples", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui:integration-tests:ui-demos", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui:ui-samples", "compose/ui/ui/samples", [BuildType.COMPOSE])
-includeProject(":concurrent:concurrent-futures", [BuildType.MAIN, BuildType.CAMERA, BuildType.COMPOSE])
-includeProject(":concurrent:concurrent-futures-ktx", [BuildType.MAIN, BuildType.CAMERA])
+includeProject(":concurrent:concurrent-futures", [BuildType.MAIN, BuildType.COMPOSE])
+includeProject(":concurrent:concurrent-futures-ktx", [BuildType.MAIN])
 includeProject(":constraintlayout:constraintlayout-compose", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose-lint", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:demos", [BuildType.COMPOSE])
@@ -770,33 +783,33 @@
 includeProject(":lifecycle:integration-tests:incrementality", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:integration-tests:lifecycle-testapp", "lifecycle/integration-tests/testapp", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:integration-tests:lifecycle-testapp-kotlin", "lifecycle/integration-tests/kotlintestapp", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-common", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-common-java8", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-common", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-common-java8", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-compiler", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-extensions", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-livedata", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-livedata-core", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-livedata-core-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-livedata-core-ktx-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-livedata", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-livedata-core", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-livedata-core-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-livedata-core-ktx-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-livedata-core-truth", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-livedata-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-livedata-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-process", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-reactivestreams", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-reactivestreams-ktx", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-runtime-compose", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples", "lifecycle/lifecycle-runtime-compose/samples", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-runtime-compose:integration-tests:lifecycle-runtime-compose-demos", [BuildType.COMPOSE])
-includeProject(":lifecycle:lifecycle-runtime-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-runtime-ktx-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-runtime-testing", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-runtime-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-runtime-ktx-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-runtime-testing", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-service", [BuildType.MAIN, BuildType.FLAN, BuildType.GLANCE])
-includeProject(":lifecycle:lifecycle-viewmodel", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-viewmodel", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples", "lifecycle/lifecycle-viewmodel-compose/samples", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:integration-tests:lifecycle-viewmodel-demos", [BuildType.COMPOSE])
-includeProject(":lifecycle:lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
-includeProject(":lifecycle:lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE, BuildType.CAMERA])
+includeProject(":lifecycle:lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lint-checks")
 includeProject(":lint-checks:integration-tests")
 includeProject(":loader:loader", [BuildType.MAIN])
@@ -1038,20 +1051,20 @@
 includeProject(":wear:watchface:watchface-style-old-api-test-stub", "wear/watchface/watchface-style/old-api-test-stub", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":webkit:integration-tests:testapp", [BuildType.MAIN])
 includeProject(":webkit:webkit", [BuildType.MAIN])
-includeProject(":window:window", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-samples", "window/window/samples", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:extensions:extensions", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:extensions:core:core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
+includeProject(":window:window", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-samples", "window/window/samples", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:extensions:extensions", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:extensions:core:core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:integration-tests:configuration-change-tests", [BuildType.MAIN, BuildType.WINDOW])
-includeProject(":window:sidecar:sidecar", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-java", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
+includeProject(":window:sidecar:sidecar", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-java", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:window-rxjava2", [BuildType.MAIN, BuildType.WINDOW])
 includeProject(":window:window-rxjava3", [BuildType.MAIN, BuildType.WINDOW])
-includeProject(":window:window-demos:demo", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-demos:demo-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-demos:demo-second-app", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
-includeProject(":window:window-testing", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA, BuildType.WINDOW])
+includeProject(":window:window-demos:demo", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-demos:demo-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-demos:demo-second-app", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
+includeProject(":window:window-testing", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":work:integration-tests:testapp", [BuildType.MAIN])
 includeProject(":work:work-benchmark", [BuildType.MAIN])
 includeProject(":work:work-gcm", [BuildType.MAIN])
@@ -1103,7 +1116,7 @@
 
 includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
 includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.KMP])
-includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA, BuildType.WEAR, BuildType.CAMERA])
+includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA, BuildType.WEAR])
 includeProject(":internal-testutils-appcompat", "testutils/testutils-appcompat", [BuildType.MAIN])
 includeProject(":internal-testutils-espresso", "testutils/testutils-espresso", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-fonts", "testutils/testutils-fonts", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
@@ -1171,6 +1184,10 @@
 // Workaround for b/203825166
 includeBuild("placeholder")
 
+// ---------------------------------------------------------------------
+// --- there should be no includeProject additions after this line -----
+// ---------------------------------------------------------------------
+
 // For a given project path add the transitive project references to the include set.
 void addReferences(String projectPath, Set<String> included) {
     if (projectPath in included) return
@@ -1179,11 +1196,33 @@
         addReferences(reference, included)
     }
 }
-Set<String> projectsToInclude = new HashSet<>()
-for (filteredProject in filteredProjects) {
-    addReferences(filteredProject, projectsToInclude)
+
+void includeRequestedProjectsAndDependencies() {
+    // Adding by including `:foo:bar` Gradle will automatically load `:foo`, so we need to
+    // add those implicit parent project references.
+    for (projectPath in projectReferences.keySet()) {
+        Set<String> newReferences = new HashSet<>()
+        for (reference in projectReferences[projectPath]) {
+            String[] segments = reference.substring(1).split(":")
+            String subpath = ""
+            for (int i = 0; i < segments.length; i++) {
+                subpath += ":" + segments[i]
+                if (allProjects.containsKey(subpath)) {
+                    newReferences.add(subpath)
+                }
+            }
+        }
+        projectReferences[projectPath].addAll(newReferences)
+    }
+
+    Set<String> projectsToInclude = new HashSet<>()
+    for (filteredProject in filteredProjects) {
+        addReferences(filteredProject, projectsToInclude)
+    }
+    for (entry in projectsToInclude) {
+        settings.include(entry)
+        project(entry).projectDir = allProjects[entry]
+    }
 }
-for (entry in projectsToInclude) {
-    settings.include(entry)
-    project(entry).projectDir = allProjects[entry]
-}
+
+includeRequestedProjectsAndDependencies()
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.kt
new file mode 100644
index 0000000..fa45f3c
--- /dev/null
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.stableaidl
+
+import androidx.stableaidl.api.Action
+import androidx.stableaidl.api.StableAidlExtension
+import androidx.stableaidl.tasks.StableAidlCheckApi
+import androidx.stableaidl.tasks.UpdateStableAidlApiTask
+import com.android.build.api.variant.SourceDirectories
+import com.android.build.gradle.internal.tasks.factory.dependsOn
+import java.io.File
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskProvider
+
+/**
+ * Internal implementation of [StableAidlExtension] that wraps task providers.
+ */
+open class StableAidlExtensionImpl : StableAidlExtension {
+    override val checkAction: Action = object : Action {
+        override fun <T : Task> before(task: TaskProvider<T>) {
+            task.dependsOn(checkTaskProvider)
+        }
+    }
+
+    override val updateAction: Action = object : Action {
+        override fun <T : Task> before(task: TaskProvider<T>) {
+            task.dependsOn(updateTaskProvider)
+        }
+    }
+
+    override var taskGroup: String? = null
+        set(taskGroup) {
+            allTasks.forEach { (_, tasks) ->
+                tasks.forEach { task ->
+                    task.configure {
+                        it.group = taskGroup
+                    }
+                }
+            }
+            field = taskGroup
+        }
+
+    override fun addStaticImportDirs(vararg dirs: File) {
+        importSourceDirs.forEach { importSourceDir ->
+            dirs.forEach { dir ->
+                importSourceDir.addStaticSourceDirectory(dir.absolutePath)
+            }
+        }
+    }
+
+    internal lateinit var updateTaskProvider: TaskProvider<UpdateStableAidlApiTask>
+    internal lateinit var checkTaskProvider: TaskProvider<StableAidlCheckApi>
+
+    internal val importSourceDirs = mutableListOf<SourceDirectories.Flat>()
+    internal val allTasks = mutableMapOf<String, Set<TaskProvider<*>>>()
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
index 91c809f..95a513e 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
@@ -16,6 +16,7 @@
 
 package androidx.stableaidl
 
+import androidx.stableaidl.api.StableAidlExtension
 import com.android.build.api.dsl.SdkComponents
 import com.android.build.api.variant.AndroidComponentsExtension
 import com.android.build.api.variant.DslExtension
@@ -29,6 +30,8 @@
 import org.gradle.api.file.RegularFile
 import org.gradle.api.provider.Provider
 
+private const val DEFAULT_VARIANT_NAME = "release"
+private const val EXTENSION_NAME = "stableaidl"
 private const val PLUGIN_DIRNAME = "stable_aidl"
 private const val GENERATED_PATH = "generated/source/$PLUGIN_DIRNAME"
 private const val INTERMEDIATES_PATH = "intermediates/${PLUGIN_DIRNAME}_parcelable"
@@ -40,6 +43,11 @@
         val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
             ?: throw GradleException("Stable AIDL plugin requires Android Gradle Plugin")
 
+        val extension = project.extensions.create(
+            EXTENSION_NAME,
+            StableAidlExtensionImpl::class.java
+        )
+
         // Obtain the AIDL executable and framework AIDL file paths using private APIs. See
         // b/268237729 for public API request, after which we can obtain them from SdkComponents.
         val base = project.extensions.getByType(BaseExtension::class.java)
@@ -130,7 +138,7 @@
                 lastReleasedApiDir,
                 generateAidlApiTask
             )
-            registerCheckAidlApi(
+            val checkAidlApiTask = registerCheckAidlApi(
                 project,
                 variant,
                 aidlExecutable,
@@ -141,12 +149,29 @@
                 generateAidlApiTask,
                 checkAidlApiReleaseTask
             )
-            registerUpdateAidlApi(
+            val updateAidlApiTask = registerUpdateAidlApi(
                 project,
                 variant,
                 lastCheckedInApiDir,
                 generateAidlApiTask
             )
+
+            if (variant.name == DEFAULT_VARIANT_NAME) {
+                extension.updateTaskProvider = updateAidlApiTask
+                extension.checkTaskProvider = checkAidlApiTask
+            }
+
+            extension.importSourceDirs.add(
+                variant.sources.getByName(SOURCE_TYPE_STABLE_AIDL_IMPORTS)
+            )
+
+            extension.allTasks[variant.name] = setOf(
+                compileAidlApiTask,
+                generateAidlApiTask,
+                checkAidlApiReleaseTask,
+                checkAidlApiTask,
+                updateAidlApiTask
+            )
         }
     }
 }
@@ -209,3 +234,19 @@
         }?.artifacts?.artifactFiles
     return listOfNotNull(aidlFiles, stableAidlFiles)
 }
+
+/**
+ * When the Stable AIDL plugin is applies to the project, runs the specified [lambda] with access to
+ * the plugin's public APIs via [StableAidlExtension].
+ *
+ * If the project does not have the Stable AIDL plugin applied, this is a no-op.
+ */
+fun Project.withStableAidlPlugin(lambda: (StableAidlExtension) -> Unit) {
+    project.plugins.withId("androidx.stableaidl") { plugin ->
+        (plugin as? StableAidlPlugin)?.let {
+            project.extensions.findByType(StableAidlExtension::class.java)?.let { ext ->
+                lambda(ext)
+            } ?: throw GradleException("Failed to locate extension for StableAidlPlugin")
+        } ?: throw GradleException("Plugin with ID \"androidx.stableaidl\" is not StableAidlPlugin")
+    }
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.kt
new file mode 100644
index 0000000..8c083ab
--- /dev/null
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.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.stableaidl.api
+
+import java.io.File
+import org.gradle.api.Incubating
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskProvider
+
+/**
+ * Extension that allows access to the StableAidl plugin's public APIs.
+ */
+interface StableAidlExtension {
+    /**
+     * An action representing the task that checks the current Stable AIDL API surface for
+     * compatibility against the previously-frozen API surface.
+     */
+    @get:Incubating
+    val checkAction: Action
+
+    /**
+     * An action representing the task that updates the frozen Stable AIDL API surface.
+     */
+    @get:Incubating
+    val updateAction: Action
+
+    /**
+     * The task group to use for Stable AIDL tasks, or `null` to hide them.
+     */
+    @get:Incubating
+    var taskGroup: String?
+
+    /**
+     * Adds static import directories to be passed to Stable AIDL.
+     *
+     * Static imports may be used in `import` statements, but are not exported to dependencies.
+     */
+    fun addStaticImportDirs(vararg dirs: File)
+}
+
+interface Action {
+    /**
+     * Runs the action before the specified [task].
+     */
+    fun <T : Task> before(task: TaskProvider<T>)
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
index 218b300..370dd6d 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
@@ -31,10 +31,12 @@
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
 
 /**
  * Transforms an AAR by adding parcelable headers.
  */
+@DisableCachingByDefault(because = "Primarily filesystem operations")
 abstract class StableAidlPackageApi : DefaultTask() {
     @get:InputFile
     @get:PathSensitive(PathSensitivity.RELATIVE)
diff --git a/test/screenshot/screenshot/build.gradle b/test/screenshot/screenshot/build.gradle
index aef8ffc..7147d25 100644
--- a/test/screenshot/screenshot/build.gradle
+++ b/test/screenshot/screenshot/build.gradle
@@ -30,10 +30,7 @@
 )
 
 dependencies {
-    bundleInside(project(
-            path: ":test:screenshot:screenshot-proto",
-            configuration: "export"
-    ))
+    bundleInside(project(path: ":test:screenshot:screenshot-proto", configuration: "export"))
     implementation("androidx.annotation:annotation:1.0.0")
     implementation("androidx.core:core:1.5.0-rc02")
 
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 c0384d2..c91c791 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
@@ -102,7 +102,7 @@
         // Short click with a time duration as a parameter (`click(long duration)`).
         UiObject2 button4 = mDevice.findObject(By.res(TEST_APP, "button4"));
         assertEquals("text4", button4.getText());
-        button4.click((long) (ViewConfiguration.getLongPressTimeout() / 1.5));
+        button4.click(50L);
         button4.wait(Until.textEquals("text4_clicked"), TIMEOUT_MS);
         assertEquals("text4_clicked", button4.getText());
 
@@ -121,8 +121,7 @@
         // Short click with two parameters (`click(Point point, long duration)`).
         UiObject2 button6 = mDevice.findObject(By.res(TEST_APP, "button6"));
         assertEquals("text6", button6.getText());
-        button6.click(getPointInsideBounds(button6),
-                (long) (ViewConfiguration.getLongPressTimeout() / 1.5));
+        button6.click(getPointInsideBounds(button6), 50L);
         button6.wait(Until.textEquals("text6_clicked"), TIMEOUT_MS);
         assertEquals("text6_clicked", button6.getText());
 
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
index 9c10f1f..d480917 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
@@ -328,8 +328,8 @@
      * @see BySelector#hasAncestor(BySelector, int)
      */
     public static @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector,
-            @IntRange(from = 1) int maxHeight) {
-        return new BySelector().hasAncestor(ancestorSelector, maxHeight);
+            @IntRange(from = 1) int maxAncestorDistance) {
+        return new BySelector().hasAncestor(ancestorSelector, maxAncestorDistance);
     }
 
     /**
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
index f653171..9ad57c2 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
@@ -59,14 +59,14 @@
 
     // Inverts parent-child relationships until a root selector is found.
     private BySelector invertSelector(BySelector selector) {
-        if (selector.mParentSelector == null) {
+        if (selector.mAncestorSelector == null) {
             return selector;
         }
-        BySelector parent = new BySelector(selector.mParentSelector);
-        selector.mParentSelector = null;
-        selector.mMaxDepth = selector.mParentHeight;
-        selector.mParentHeight = null;
-        return invertSelector(parent.hasDescendant(selector));
+        BySelector ancestor = new BySelector(selector.mAncestorSelector);
+        selector.mAncestorSelector = null;
+        selector.mMaxDepth = selector.mMaxAncestorDistance;
+        selector.mMaxAncestorDistance = null;
+        return invertSelector(ancestor.hasDescendant(selector));
     }
 
     /**
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 c6997db..63a0a49 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
@@ -51,9 +51,9 @@
     Integer mMinDepth;
     Integer mMaxDepth;
 
-    // Parent selector
-    BySelector mParentSelector;
-    Integer mParentHeight;
+    // Ancestor selector
+    BySelector mAncestorSelector;
+    Integer mMaxAncestorDistance;
 
     // Child selectors
     final List<BySelector> mChildSelectors = new LinkedList<>();
@@ -87,9 +87,9 @@
         mMinDepth = original.mMinDepth;
         mMaxDepth = original.mMaxDepth;
 
-        mParentSelector = original.mParentSelector == null ? null :
-                new BySelector(original.mParentSelector);
-        mParentHeight = original.mParentHeight;
+        mAncestorSelector = original.mAncestorSelector == null ? null :
+                new BySelector(original.mAncestorSelector);
+        mMaxAncestorDistance = original.mMaxAncestorDistance;
 
         for (BySelector childSelector : original.mChildSelectors) {
             mChildSelectors.add(new BySelector(childSelector));
@@ -595,10 +595,10 @@
      */
     public @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector) {
         checkNotNull(ancestorSelector, "ancestorSelector cannot be null");
-        if (mParentSelector != null) {
+        if (mAncestorSelector != null) {
             throw new IllegalStateException("Parent/ancestor selector is already defined");
         }
-        mParentSelector = ancestorSelector;
+        mAncestorSelector = ancestorSelector;
         return this;
     }
 
@@ -607,14 +607,16 @@
      * it has an ancestor element which matches the {@code ancestorSelector} and all other
      * criteria for this selector are met.
      *
-     * @param ancestorSelector The selector used to find a matching ancestor element.
-     * @param maxHeight        The maximum height above the element to search for the ancestor.
+     * @param ancestorSelector    The selector used to find a matching ancestor element.
+     * @param maxAncestorDistance The maximum distance between the element and its relevant
+     *                            ancestor in the view hierarchy, e.g. 1 only matches the parent
+     *                            element, 2 matches parent or grandparent.
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector,
-            @IntRange(from = 1) int maxHeight) {
+            @IntRange(from = 1) int maxAncestorDistance) {
         hasAncestor(ancestorSelector);
-        mParentHeight = maxHeight;
+        mMaxAncestorDistance = maxAncestorDistance;
         return this;
     }
 
@@ -626,7 +628,7 @@
      *
      * @param childSelector The selector used to find a matching child element.
      * @return A reference to this {@link BySelector}.
-     * @throws IllegalArgumentException if the selector has a parent selector
+     * @throws IllegalArgumentException if the selector has a parent/ancestor selector
      */
     public @NonNull BySelector hasChild(@NonNull BySelector childSelector) {
         checkNotNull(childSelector, "childSelector cannot be null");
@@ -641,11 +643,11 @@
      *
      * @param descendantSelector The selector used to find a matching descendant element.
      * @return A reference to this {@link BySelector}.
-     * @throws IllegalArgumentException if the selector has a parent selector
+     * @throws IllegalArgumentException if the selector has a parent/ancestor selector
      */
     public @NonNull BySelector hasDescendant(@NonNull BySelector descendantSelector) {
         checkNotNull(descendantSelector, "descendantSelector cannot be null");
-        if (descendantSelector.mParentSelector != null) {
+        if (descendantSelector.mAncestorSelector != null) {
             // Search root is ambiguous with nested parent selectors.
             throw new IllegalArgumentException(
                     "Nested parent/ancestor selectors are not supported");
@@ -663,7 +665,7 @@
      * @param descendantSelector The selector used to find a matching descendant element.
      * @param maxDepth The maximum depth under the element to search the descendant.
      * @return A reference to this {@link BySelector}.
-     * @throws IllegalArgumentException if the selector has a parent selector
+     * @throws IllegalArgumentException if the selector has a parent/ancestor selector
      */
     public @NonNull BySelector hasDescendant(@NonNull BySelector descendantSelector, int maxDepth) {
         hasDescendant(descendantSelector);
@@ -721,9 +723,9 @@
         if (mSelected != null) {
             builder.append("SELECTED='").append(mSelected).append("', ");
         }
-        if (mParentSelector != null) {
-            builder.append("PARENT='")
-                    .append(mParentSelector.toString().substring(11))
+        if (mAncestorSelector != null) {
+            builder.append("ANCESTOR='")
+                    .append(mAncestorSelector.toString().substring(11))
                     .append("', ");
         }
         for (BySelector childSelector : mChildSelectors) {
@@ -741,4 +743,3 @@
         return value;
     }
 }
-
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
index 8c8a203..1c165d3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
@@ -146,8 +146,8 @@
 
         // Loop
         MotionEvent event;
-        for (long elapsedTime = 0; !pending.isEmpty() || !active.isEmpty();
-                elapsedTime = SystemClock.uptimeMillis() - startTime) {
+        long elapsedTime = 0;
+        for (; !pending.isEmpty() || !active.isEmpty(); elapsedTime += injectionDelay) {
 
             // Touch up any completed pointers
             while (!active.isEmpty()
@@ -168,7 +168,7 @@
                     action = MotionEvent.ACTION_POINTER_UP
                             + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
                 }
-                event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,
+                event = getMotionEvent(startTime, SystemClock.uptimeMillis(), action, properties,
                         coordinates, gesture.displayId());
                 getDevice().getUiAutomation().injectInputEvent(event, false);
 
@@ -183,8 +183,9 @@
 
             }
             if (!active.isEmpty()) {
-                event = getMotionEvent(startTime, startTime + elapsedTime, MotionEvent.ACTION_MOVE,
-                        properties, coordinates, active.peek().displayId());
+                event = getMotionEvent(startTime, SystemClock.uptimeMillis(),
+                        MotionEvent.ACTION_MOVE, properties, coordinates,
+                        active.peek().displayId());
                 getDevice().getUiAutomation().injectInputEvent(event, false);
             }
 
@@ -205,7 +206,7 @@
                     action = MotionEvent.ACTION_POINTER_DOWN
                             + ((properties.size() - 1) << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
                 }
-                event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,
+                event = getMotionEvent(startTime, SystemClock.uptimeMillis(), action, properties,
                         coordinates, gesture.displayId());
                 getDevice().getUiAutomation().injectInputEvent(event, false);
 
@@ -215,6 +216,12 @@
 
             SystemClock.sleep(injectionDelay);
         }
+
+        long upTime = SystemClock.uptimeMillis() - startTime;
+        if (upTime >= 2 * elapsedTime) {
+            Log.w(TAG, String.format("Gestures took longer than expected (%dms >> %dms), device "
+                    + "might be in a busy state.", upTime, elapsedTime));
+        }
     }
 
     /** Helper function to obtain a MotionEvent. */
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
index c517d99..332cf06 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
@@ -17,7 +17,7 @@
 package androidx.tv.integration.playground
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.focusable
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.width
@@ -37,6 +37,6 @@
             .width(200.dp)
             .height(150.dp)
             .drawBorderOnFocus()
-            .focusable()
+            .clickable { }
     )
 }
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
index 64f92b9..6f6d535 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
@@ -16,7 +16,6 @@
 
 package androidx.tv.integration.playground
 
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -42,8 +41,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
-import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.material3.Carousel
 import androidx.tv.material3.CarouselDefaults
 import androidx.tv.material3.CarouselState
@@ -88,9 +88,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class,
-    ExperimentalTvFoundationApi::class
-)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
     val backgrounds = listOf(
@@ -105,41 +103,47 @@
     )
 
     val carouselState = remember { CarouselState() }
-    FocusGroup {
-        Carousel(
-            itemCount = backgrounds.size,
-            carouselState = carouselState,
-            modifier = modifier
-                .height(300.dp)
-                .fillMaxWidth(),
-            carouselIndicator = {
-                CarouselDefaults.IndicatorRow(
-                    itemCount = backgrounds.size,
-                    activeItemIndex = carouselState.activeItemIndex,
+    Carousel(
+        itemCount = backgrounds.size,
+        carouselState = carouselState,
+        modifier = modifier
+            .height(300.dp)
+            .fillMaxWidth(),
+        carouselIndicator = {
+            CarouselDefaults.IndicatorRow(
+                itemCount = backgrounds.size,
+                activeItemIndex = carouselState.activeItemIndex,
+                modifier = Modifier
+                    .align(Alignment.BottomEnd)
+                    .padding(16.dp),
+            )
+        }
+    ) { itemIndex ->
+        CarouselItem(
+            modifier = Modifier.semantics {
+                contentDescription = "Featured Content"
+            },
+            background = {
+                Box(
                     modifier = Modifier
-                        .align(Alignment.BottomEnd)
-                        .padding(16.dp),
+                        .background(backgrounds[itemIndex])
+                        .fillMaxSize()
                 )
-            }
-        ) { itemIndex ->
-            CarouselItem(
-                background = {
-                    Box(
-                        modifier = Modifier
-                            .background(backgrounds[itemIndex])
-                            .fillMaxSize()
-                    )
-                },
-                modifier =
-                if (itemIndex == 0)
-                    Modifier.initiallyFocused()
-                else
-                    Modifier.restorableFocus()
+            },
+        ) {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .padding(20.dp),
+                contentAlignment = Alignment.BottomStart
             ) {
-                Box(modifier = Modifier) {
-                    OverlayButton(
-                        modifier = Modifier
-                    )
+                Column {
+                    Text(text = "This is sample text content.", color = Color.Yellow)
+                    Text(text = "Sample description.", color = Color.Yellow)
+                    Row {
+                        OverlayButton(text = "Play")
+                        OverlayButton(text = "Add to Watchlist")
+                    }
                 }
             }
         }
@@ -147,14 +151,16 @@
 }
 
 @Composable
-private fun OverlayButton(modifier: Modifier = Modifier) {
+private fun OverlayButton(modifier: Modifier = Modifier, text: String = "Play") {
     var isFocused by remember { mutableStateOf(false) }
 
     Button(
         onClick = { },
         modifier = modifier
-            .onFocusChanged { isFocused = it.isFocused }
-            .padding(40.dp)
+            .onFocusChanged {
+                isFocused = it.isFocused
+            }
+            .padding(20.dp)
             .border(
                 width = 2.dp,
                 color = if (isFocused) Color.Red else Color.Transparent,
@@ -162,6 +168,6 @@
             )
             .padding(vertical = 2.dp, horizontal = 5.dp)
     ) {
-        Text(text = "Play")
+        Text(text = text)
     }
 }
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
index b8ed37f..ac3d021 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
@@ -16,24 +16,19 @@
 
 package androidx.tv.integration.playground
 
-import android.util.Log
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.foundation.lazy.list.TvLazyColumn
@@ -54,7 +49,6 @@
 private fun SampleImmersiveList() {
     val immersiveListHeight = 300.dp
     val cardSpacing = 10.dp
-    val cardWidth = 200.dp
     val cardHeight = 150.dp
     val backgrounds = listOf(
         Color.Red,
@@ -76,27 +70,24 @@
                 )
             }
         ) {
-            Row(horizontalArrangement = Arrangement.spacedBy(cardSpacing)) {
-                backgrounds.forEachIndexed { index, backgroundColor ->
-                    var isFocused by remember { mutableStateOf(false) }
+            LazyRow(
+                horizontalArrangement = Arrangement.spacedBy(cardSpacing),
+                modifier = Modifier.lazyListSemantics(1, backgrounds.count())
+            ) {
+                itemsIndexed(backgrounds) { index, backgroundColor ->
+                    val cardModifier =
+                        if (index == 0)
+                            Modifier.initiallyFocused()
+                        else
+                            Modifier.restorableFocus()
 
-                    Box(
-                        modifier = Modifier
-                            .background(backgroundColor)
-                            .width(cardWidth)
-                            .height(cardHeight)
-                            .border(5.dp, Color.White.copy(alpha = if (isFocused) 1f else 0.3f))
-                            .then(
-                                if (index == 0)
-                                    Modifier.initiallyFocused()
-                                else
-                                    Modifier.restorableFocus()
-                            )
-                            .onFocusChanged { isFocused = it.isFocused }
-                            .immersiveListItem(index)
-                            .clickable {
-                                Log.d("ImmersiveList", "Item $index was clicked")
+                    Card(
+                        modifier = cardModifier
+                            .semantics {
+                                collectionItemInfo = CollectionItemInfo(0, 1, index, 1)
                             }
+                            .immersiveListItem(index),
+                        backgroundColor = backgroundColor
                     )
                 }
             }
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
index 7ecfec8..2fbddf4 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
@@ -25,11 +25,17 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.foundation.PivotOffsets
 import androidx.tv.foundation.lazy.list.TvLazyColumn
 import androidx.tv.foundation.lazy.list.TvLazyRow
+import androidx.tv.foundation.lazy.list.itemsIndexed
 
 const val rowsCount = 20
 const val columnsCount = 100
@@ -58,19 +64,35 @@
     val backgroundColors = List(columnsCount) { colors.random() }
 
     FocusGroup {
-        TvLazyRow(modifier, horizontalArrangement = Arrangement.spacedBy(10.dp)) {
-            backgroundColors.forEachIndexed { index, backgroundColor ->
-                item {
-                    Card(
-                        backgroundColor = backgroundColor,
-                        modifier =
-                        if (index == 0)
-                            Modifier.initiallyFocused()
-                        else
-                            Modifier.restorableFocus()
-                    )
-                }
+        TvLazyRow(
+            modifier = modifier.lazyListSemantics(1, columnsCount),
+            horizontalArrangement = Arrangement.spacedBy(10.dp)
+        ) {
+            itemsIndexed(backgroundColors) { index, item ->
+                val cardModifier =
+                    if (index == 0)
+                        Modifier.initiallyFocused()
+                    else
+                        Modifier.restorableFocus()
+
+                Card(
+                    modifier = cardModifier.semantics {
+                        collectionItemInfo = CollectionItemInfo(0, 1, index, 1)
+                    },
+                    backgroundColor = item
+                )
             }
         }
     }
 }
+
+@Composable
+fun Modifier.lazyListSemantics(rowCount: Int = -1, columnCount: Int = -1): Modifier {
+    return this.then(
+        remember(rowCount, columnCount) {
+            Modifier.semantics {
+                collectionInfo = CollectionInfo(rowCount, columnCount)
+            }
+        }
+    )
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
index 86865b3..1257c1b 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -28,11 +29,11 @@
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.KeyboardArrowLeft
 import androidx.compose.material.icons.filled.KeyboardArrowRight
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -43,14 +44,16 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.selected
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
-import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.material3.DrawerValue
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Icon
@@ -110,7 +113,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun Sidebar(
     drawerValue: DrawerValue,
@@ -128,8 +131,10 @@
     Column(
         modifier = Modifier
             .fillMaxHeight()
-            .background(pageColor),
+            .background(pageColor)
+            .selectableGroup(),
         horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.spacedBy(10.dp)
     ) {
         NavigationItem(
             imageVector = Icons.Default.KeyboardArrowRight,
@@ -160,15 +165,19 @@
 ) {
     var isFocused by remember { mutableStateOf(false) }
 
-    Button(
-        onClick = { selectedIndex.value = index },
+    Box(
         modifier = modifier
-            .onFocusChanged { isFocused = it.isFocused },
-        colors = ButtonDefaults.filledTonalButtonColors(
-            containerColor = if (isFocused) Color.White else Color.Transparent,
-        )
+            .clip(RoundedCornerShape(10.dp))
+            .onFocusChanged { isFocused = it.isFocused }
+            .background(if (isFocused) Color.White else Color.Transparent)
+            .semantics(mergeDescendants = true) {
+                selected = selectedIndex.value == index
+            }
+            .clickable {
+                selectedIndex.value = index
+            }
     ) {
-        Box(modifier = Modifier) {
+        Box(modifier = Modifier.padding(10.dp)) {
             Row(
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement = Arrangement.spacedBy(5.dp),
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
index 51f87fd..afc1361 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
@@ -1489,15 +1489,24 @@
                 userScrollEnabled = false,
             ) {
                 items(5) {
-                    Spacer(Modifier.size(itemSize).testTag("$it"))
+                    Box(
+                        modifier = Modifier
+                            .size(itemSize)
+                            .border(2.dp, Color.Blue)
+                            .testTag("$it")
+                            .focusable()
+                    ) {
+                        BasicText("$it")
+                    }
                 }
             }
         }
 
-        rule.keyPress(1)
+        rule.onNodeWithTag("2").performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.keyPress(2)
 
-        rule.onNodeWithTag("1")
-            .assertStartPositionInRootIsEqualTo(itemSize)
+        rule.onNodeWithTag("1").assertIsDisplayed()
+        rule.onNodeWithTag("3").assertIsNotDisplayed()
     }
 
     @Test
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/ContentInViewModifier.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/ContentInViewModifier.kt
index 6c22786..716e8f8 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/ContentInViewModifier.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/ContentInViewModifier.kt
@@ -59,7 +59,8 @@
     private val orientation: Orientation,
     private val scrollState: ScrollableState,
     private val reverseDirection: Boolean,
-    private val pivotOffsets: PivotOffsets
+    private val pivotOffsets: PivotOffsets,
+    private val userScrollEnabled: Boolean
 ) : BringIntoViewResponder,
     OnRemeasuredModifier,
     OnPlacedModifier {
@@ -352,6 +353,8 @@
         trailingEdgeOfItemRequestingFocus: Float,
         containerSize: Float
     ): Float {
+        if (!userScrollEnabled) return 0f
+
         val sizeOfItemRequestingFocus =
             abs(trailingEdgeOfItemRequestingFocus - leadingEdgeOfItemRequestingFocus)
         val childSmallerThanParent = sizeOfItemRequestingFocus <= containerSize
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/ScrollableWithPivot.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/ScrollableWithPivot.kt
index 635441e..f7f95a3 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/ScrollableWithPivot.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/ScrollableWithPivot.kt
@@ -79,9 +79,15 @@
     factory = {
         val coroutineScope = rememberCoroutineScope()
         val keepFocusedChildInViewModifier =
-            remember(coroutineScope, orientation, state, reverseDirection, pivotOffsets) {
+            remember(coroutineScope, orientation, state, reverseDirection, pivotOffsets, enabled) {
                 ContentInViewModifier(
-                    coroutineScope, orientation, state, reverseDirection, pivotOffsets)
+                    scope = coroutineScope,
+                    orientation = orientation,
+                    scrollState = state,
+                    reverseDirection = reverseDirection,
+                    pivotOffsets = pivotOffsets,
+                    userScrollEnabled = enabled
+                )
             }
 
         Modifier
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index bebfaef..e2cc7d6 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -410,6 +410,18 @@
     field public static final androidx.tv.material3.OutlinedIconButtonDefaults INSTANCE;
   }
 
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class RadioButtonColors {
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor, optional long disabledSelectedColor, optional long disabledUnselectedColor);
+    field public static final androidx.tv.material3.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.tv.material3.RadioButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   @androidx.tv.material3.ExperimentalTvMaterial3Api public sealed interface ScrollPauseHandle {
     method public void resumeAutoScroll();
   }
@@ -450,6 +462,20 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
 
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SwitchColors {
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
+    field public static final androidx.tv.material3.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.tv.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class TabColors {
   }
 
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 7b7119b..0b86754 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -38,7 +38,7 @@
     api("androidx.compose.foundation:foundation-layout:$composeVersion")
     api("androidx.compose.material:material-icons-core:$composeVersion")
     api("androidx.compose.ui:ui-graphics:$composeVersion")
-    api("androidx.compose.ui:ui-text:$composeVersion")
+    api(project(":compose:ui:ui-text"))
     api("androidx.compose.ui:ui-util:$composeVersion")
     api(project(":tv:tv-foundation"))
 
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
index 10080e0..2f80311 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.tv.material3
 
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -50,7 +49,7 @@
     @get:Rule
     val rule = createComposeRule()
 
-    @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+    @OptIn(ExperimentalTvMaterial3Api::class)
     @Test
     fun carouselItem_parentContainerGainsFocused_onBackPress() {
         val containerBoxTag = "container-box"
@@ -69,7 +68,8 @@
             ) {
                 CarouselScope(carouselState = carouselState)
                     .CarouselItem(
-                        modifier = Modifier.testTag(carouselItemTag),
+                        modifier = Modifier
+                            .testTag(carouselItemTag),
                         background = {
                             Box(
                                 modifier = Modifier
@@ -87,7 +87,8 @@
         rule.waitForIdle()
 
         // Check if overlay button in carousel item is focused
-        rule.onNodeWithTag(sampleButtonTag).assertIsFocused()
+        rule.onNodeWithTag(sampleButtonTag, useUnmergedTree = true)
+            .assertIsFocused()
 
         // Trigger back press
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
index 7582b2c..1c863ef 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
@@ -67,6 +67,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
+import kotlin.math.abs
 import kotlinx.coroutines.delay
 import org.junit.Rule
 import org.junit.Test
@@ -275,7 +276,6 @@
         rule.onNodeWithText("Text 2").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_pagerIndicatorDisplayed() {
         rule.setContent {
@@ -287,7 +287,6 @@
         rule.onNodeWithTag("indicator").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_withAnimatedContent_successfulTransition() {
         rule.setContent {
@@ -308,7 +307,6 @@
         rule.onNodeWithText("PLAY").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_withAnimatedContent_successfulFocusIn() {
         rule.setContent {
@@ -326,8 +324,9 @@
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
-        rule.onNodeWithText("Play 0").assertIsDisplayed()
-        rule.onNodeWithText("Play 0").assertIsFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true)
+            .assertIsDisplayed()
+            .assertIsFocused()
     }
 
     @Test
@@ -353,7 +352,7 @@
         rule.waitForIdle()
 
         // Check if the overlay button is focused
-        rule.onNodeWithText("Button-1").assertIsFocused()
+        rule.onNodeWithText("Button-1", useUnmergedTree = true).assertIsFocused()
 
         // Trigger back press event to exit focus
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
@@ -361,13 +360,12 @@
         rule.waitForIdle()
 
         // Check if carousel loses focus and parent container gains focus
-        rule.onNodeWithText("Button-1").assertIsNotFocused()
+        rule.onNodeWithText("Button-1", useUnmergedTree = true).assertIsNotFocused()
         rule.onNodeWithTag("box-container").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
-    fun carousel_withCarouselItem_parentContainerGainsFocus_onBackPress() {
+    fun carousel_withCarouselItem_parentContainerGainsFocusOnBackPress() {
         rule.setContent {
             Box(modifier = Modifier
                 .testTag("box-container")
@@ -391,7 +389,7 @@
         rule.waitForIdle()
 
         // Check if the overlay button is focused
-        rule.onNodeWithText("Play 0").assertIsFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true).assertIsFocused()
 
         // Trigger back press event to exit focus
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
@@ -399,11 +397,10 @@
         rule.waitForIdle()
 
         // Check if carousel loses focus and parent container gains focus
-        rule.onNodeWithText("Play 0").assertIsNotFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true).assertIsNotFocused()
         rule.onNodeWithTag("box-container").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_scrollToRegainFocus_checkBringIntoView() {
         val focusRequester = FocusRequester()
@@ -491,7 +488,6 @@
         assertThat(checkNodeCompletelyVisible(rule, "featured-carousel")).isTrue()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_zeroItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
@@ -502,7 +498,6 @@
         rule.onNodeWithTag(testTag).assertExists()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_oneItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
@@ -514,7 +509,7 @@
     }
 
     @Test
-    fun carousel_manualScrolling_withFocusableItemsOnTop() {
+    fun carousel_manualScrollingWithFocusableItemsOnTop_focusStaysWithinCarousel() {
         rule.setContent {
             Column {
                 Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
@@ -551,8 +546,7 @@
 
         // Check that item 2 is in view and button 2 has focus
         rule.onNodeWithText("Button-2").assertIsDisplayed()
-        // TODO: Fix button 2 isn't gaining focus
-        // rule.onNodeWithText("Button-2").assertIsFocused()
+        rule.onNodeWithText("Button-2").assertIsFocused()
 
         // Check if the first focusable element in parent has focus
         rule.onNodeWithText("Row-button-1").assertIsNotFocused()
@@ -571,17 +565,24 @@
         rule.onNodeWithText("Button-1").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
-    fun carousel_manualScrolling_fastMultipleKeyPresses() {
+    fun carousel_manualScrollingFastMultipleKeyPresses_focusStaysWithinCarousel() {
         val carouselState = CarouselState()
         val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
+        var numberOfTimesTabGainedFocus = 0
 
         rule.setContent {
             var selectedTabIndex by remember { mutableStateOf(0) }
 
             Column {
-                TabRow(selectedTabIndex = selectedTabIndex) {
+                TabRow(
+                    modifier = Modifier.onFocusChanged {
+                        if (it.hasFocus || it.isFocused) {
+                            numberOfTimesTabGainedFocus++
+                        }
+                    },
+                    selectedTabIndex = selectedTabIndex
+                ) {
                     tabs.forEachIndexed { index, tab ->
                         Tab(
                             selected = index == selectedTabIndex,
@@ -599,33 +600,36 @@
         }
 
         rule.waitForIdle()
-        rule.onNodeWithTag("pager").performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.onNodeWithText("Play 0").performSemanticsAction(SemanticsActions.RequestFocus)
         rule.waitForIdle()
 
         val itemProgression = listOf(6, 3, -4, 3, -6, 5, 3)
+        // reset the counter at test start.
+        numberOfTimesTabGainedFocus = 0
 
         itemProgression.forEach {
-            if (it < 0) {
-                performKeyPress(NativeKeyEvent.KEYCODE_DPAD_LEFT, it * -1)
-            } else {
-                performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT, it)
-            }
+            performKeyPress(
+                if (it < 0) NativeKeyEvent.KEYCODE_DPAD_LEFT else NativeKeyEvent.KEYCODE_DPAD_RIGHT,
+                abs(it)
+            )
+            rule.waitForIdle()
         }
 
         rule.mainClock.advanceTimeBy(animationTime)
 
         val finalItem = itemProgression.sum()
-        rule.onNodeWithText("Play $finalItem").assertIsFocused()
+        assertThat(numberOfTimesTabGainedFocus).isEqualTo(0)
+        rule.onNodeWithText("Play $finalItem", useUnmergedTree = true).assertIsFocused()
 
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT, 3)
 
         rule.mainClock.advanceTimeBy((animationTime) * 3)
 
-        rule.onNodeWithText("Play ${finalItem + 3}").assertIsFocused()
+        rule.onNodeWithText("Play ${finalItem + 3}", useUnmergedTree = true).assertIsFocused()
     }
 
     @Test
-    fun carousel_manualScrolling_onDpadLongPress() {
+    fun carousel_manualScrollingDpadLongPress_moveOnlyOneSlide() {
         rule.setContent {
             SampleCarousel(itemCount = 6) { index ->
                 SampleButton("Button ${index + 1}")
@@ -670,7 +674,7 @@
     }
 
     @Test
-    fun carousel_manualScrolling_ltr() {
+    fun carousel_manualScrollingLtr_RightMovesToNextSlideLeftMovesToPrevSlide() {
         rule.setContent {
             SampleCarousel { index ->
                 SampleButton("Button ${index + 1}")
@@ -716,7 +720,7 @@
     }
 
     @Test
-    fun carousel_manualScrolling_rtl() {
+    fun carousel_manualScrollingRtl_LeftMovesToNextSlideRightMovesToPrevSlide() {
         rule.setContent {
             CompositionLocalProvider(
                 LocalLayoutDirection provides LayoutDirection.Rtl
@@ -796,9 +800,30 @@
 
         rule.waitUntil(timeoutMillis = 5000) { itemChanges > minSuccessfulItemChanges }
     }
+
+    @Test
+    fun carousel_slideWithTwoButtonsInARow_focusMovesWithinSlideAndChangesSlideOnlyOnFocusExit() {
+        rule.setContent {
+            // No AutoScrolling
+            SampleCarousel(timeToDisplayItemMillis = Long.MAX_VALUE) {
+                Row {
+                    SampleButton("Left Button ${it + 1}")
+                    SampleButton("Right Button ${it + 1}")
+                }
+            }
+        }
+
+        rule.onNodeWithText("Left Button 1").performSemanticsAction(SemanticsActions.RequestFocus)
+        performKeyPress(KeyEvent.KEYCODE_DPAD_RIGHT)
+        // focus should have moved from left to right button
+        rule.onNodeWithText("Right Button 1").assertIsFocused()
+        performKeyPress(KeyEvent.KEYCODE_DPAD_RIGHT)
+        // slide should have changed.
+        rule.onNodeWithText("Left Button 2").assertIsFocused()
+    }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun SampleCarousel(
     carouselState: CarouselState = remember { CarouselState() },
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonScreenshotTest.kt
new file mode 100644
index 0000000..1ee4b70
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonScreenshotTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.InputMode
+import androidx.compose.ui.input.InputModeManager
+import androidx.compose.ui.platform.LocalInputModeManager
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performMouseInput
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@MediumTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalTestApi::class, ExperimentalTvMaterial3Api::class)
+class RadioButtonScreenshotTest(private val scheme: ColorSchemeWrapper) {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    private val wrap = Modifier.wrapContentSize(Alignment.TopStart)
+    private val wrapperTestTag = "radioButtonWrapper"
+
+    @Test
+    fun radioButton_selected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(selected = true, onClick = {})
+            }
+        }
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_selected")
+    }
+
+    @Test
+    fun radioButton_notSelected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(selected = false, onClick = {})
+            }
+        }
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_notSelected")
+    }
+
+    @Test
+    fun radioButton_hovered() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(selected = false, onClick = {})
+            }
+        }
+        rule.onNodeWithTag(wrapperTestTag).performMouseInput {
+            enter(center)
+        }
+
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_hovered")
+    }
+
+    @Test
+    fun radioButton_focused() {
+        val focusRequester = FocusRequester()
+        var localInputModeManager: InputModeManager? = null
+
+        rule.setMaterialContent(scheme.colorScheme) {
+            localInputModeManager = LocalInputModeManager.current
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(
+                    selected = false,
+                    onClick = {},
+                    modifier = Modifier
+                        .focusRequester(focusRequester)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            localInputModeManager!!.requestInputMode(InputMode.Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_focused")
+    }
+
+    @Test
+    fun radioButton_disabled_selected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(selected = true, onClick = {}, enabled = false)
+            }
+        }
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_disabled_selected")
+    }
+
+    @Test
+    fun radioButton_disabled_notSelected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                RadioButton(selected = false, onClick = {}, enabled = false)
+            }
+        }
+        assertSelectableAgainstGolden("radioButton_${scheme.name}_disabled_notSelected")
+    }
+
+    private fun assertSelectableAgainstGolden(goldenName: String) {
+        rule.onNodeWithTag(wrapperTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenName)
+    }
+
+    // Provide the ColorScheme and their name parameter in a ColorSchemeWrapper.
+    // This makes sure that the default method name and the initial Scuba image generated
+    // name is as expected.
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = arrayOf(
+            ColorSchemeWrapper("lightTheme", lightColorScheme()),
+            ColorSchemeWrapper("darkTheme", darkColorScheme()),
+        )
+    }
+
+    class ColorSchemeWrapper constructor(val name: String, val colorScheme: ColorScheme) {
+        override fun toString(): String {
+            return name
+        }
+    }
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonTest.kt
new file mode 100644
index 0000000..75cd25b
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/RadioButtonTest.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.focused
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.isFocusable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
+class RadioButtonTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val itemOne = "Bar"
+    private val itemTwo = "Foo"
+    private val itemThree = "Sap"
+
+    private fun SemanticsNodeInteraction.assertHasSelectedSemantics(): SemanticsNodeInteraction =
+        assertIsSelected()
+
+    private fun SemanticsNodeInteraction.assertHasUnSelectedSemantics(): SemanticsNodeInteraction =
+        assertIsNotSelected()
+
+    private val options = listOf(itemOne, itemTwo, itemThree)
+
+    @Test
+    fun radioGroupTest_defaultSemantics() {
+        val selected = mutableStateOf(itemOne)
+
+        rule.setContent {
+            LightMaterialTheme {
+                Column {
+                    options.forEach { item ->
+                        RadioButton(
+                            modifier = Modifier.testTag(item),
+                            selected = (selected.value == item),
+                            onClick = { selected.value = item }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(itemOne)
+            .assert(
+                SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.RadioButton)
+            )
+            .assertHasSelectedSemantics()
+        rule.onNodeWithTag(itemTwo)
+            .assertHasUnSelectedSemantics()
+            .assert(
+                SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.RadioButton)
+            )
+        rule.onNodeWithTag(itemThree)
+            .assert(
+                SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.RadioButton)
+            )
+            .assertHasUnSelectedSemantics()
+    }
+
+    @Test
+    fun radioGroupTest_ensureUnselectable() {
+        val selected = mutableStateOf(itemOne)
+
+        rule.setContent {
+            LightMaterialTheme {
+                Column {
+                    options.forEach { item ->
+                        RadioButton(
+                            modifier = Modifier.testTag(item),
+                            selected = (selected.value == item),
+                            onClick = { selected.value = item }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(itemOne)
+            .assertHasSelectedSemantics()
+            .performClick()
+            .assertHasSelectedSemantics()
+
+        rule.onNodeWithTag(itemTwo)
+            .assertHasUnSelectedSemantics()
+
+        rule.onNodeWithTag(itemThree)
+            .assertHasUnSelectedSemantics()
+    }
+
+    @Test
+    fun radioGroupTest_clickSelect() {
+        val selected = mutableStateOf(itemOne)
+        rule.setContent {
+            LightMaterialTheme {
+                Column {
+                    options.forEach { item ->
+                        RadioButton(
+                            modifier = Modifier.testTag(item),
+                            selected = (selected.value == item),
+                            onClick = { selected.value = item }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(itemTwo)
+            .assertHasUnSelectedSemantics()
+            .performClick()
+            .assertHasSelectedSemantics()
+
+        rule.onNodeWithTag(itemOne)
+            .assertHasUnSelectedSemantics()
+
+        rule.onNodeWithTag(itemThree)
+            .assertHasUnSelectedSemantics()
+    }
+
+    @Test
+    fun radioGroup_untoggleableAndMergeable_whenNullLambda() {
+        val parentTag = "parent"
+        rule.setContent {
+            LightMaterialTheme {
+                Column(
+                    Modifier
+                        .semantics(mergeDescendants = true) {}
+                        .testTag(parentTag)) {
+                    RadioButton(
+                        selected = true,
+                        onClick = null,
+                        modifier = Modifier.semantics { focused = true }
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(parentTag)
+            .assertHasNoClickAction()
+            .assert(isFocusable()) // Check merged into parent
+    }
+
+    @Test
+    @LargeTest
+    fun radioGroupTest_clickSelectTwoDifferentItems() {
+        val selected = mutableStateOf(itemOne)
+
+        rule.setContent {
+            LightMaterialTheme {
+                Column {
+                    options.forEach { item ->
+                        RadioButton(
+                            modifier = Modifier.testTag(item),
+                            selected = (selected.value == item),
+                            onClick = { selected.value = item }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(itemTwo)
+            .assertHasUnSelectedSemantics()
+            .performClick()
+            .assertHasSelectedSemantics()
+
+        rule.onNodeWithTag(itemOne)
+            .assertHasUnSelectedSemantics()
+
+        rule.onNodeWithTag(itemThree)
+            .assertHasUnSelectedSemantics()
+            .performClick()
+            .assertHasSelectedSemantics()
+
+        rule.onNodeWithTag(itemOne)
+            .assertHasUnSelectedSemantics()
+
+        rule.onNodeWithTag(itemTwo)
+            .assertHasUnSelectedSemantics()
+    }
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchScreenshotTest.kt
new file mode 100644
index 0000000..dfa1fcf
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchScreenshotTest.kt
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.InputMode
+import androidx.compose.ui.input.InputModeManager
+import androidx.compose.ui.platform.LocalInputModeManager
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performMouseInput
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalTestApi::class, ExperimentalTvMaterial3Api::class)
+class SwitchScreenshotTest(private val scheme: ColorSchemeWrapper) {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+    private val wrapperTestTag = "switchWrapper"
+
+    private val wrapperModifier = Modifier
+        .wrapContentSize(Alignment.TopStart)
+        .testTag(wrapperTestTag)
+
+    @Test
+    fun switchTest_checked() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(checked = true, onCheckedChange = { })
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_checked")
+    }
+
+    @Test
+    fun switchTest_checked_rtl() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                    Switch(checked = true, onCheckedChange = { })
+                }
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_checked_rtl")
+    }
+
+    @Test
+    fun switchTest_checked_customThumbColor() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(
+                    checked = true,
+                    onCheckedChange = { },
+                    colors = SwitchDefaults.colors(checkedThumbColor = Color.Green)
+                )
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_checked_customThumbColor")
+    }
+
+    @Test
+    fun switchTest_unchecked() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(checked = false, onCheckedChange = { })
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_unchecked")
+    }
+
+    @Test
+    fun switchTest_unchecked_rtl() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                    Switch(checked = false, onCheckedChange = { })
+                }
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_unchecked_rtl")
+    }
+
+    @Test
+    fun switchTest_disabled_checked() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(checked = true, enabled = false, onCheckedChange = { })
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_disabled_checked")
+    }
+
+    @Test
+    fun switchTest_disabled_unchecked() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(checked = false, enabled = false, onCheckedChange = { })
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_disabled_unchecked")
+    }
+
+    @Test
+    fun switchTest_hover() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrapperModifier) {
+                Switch(
+                    checked = true,
+                    onCheckedChange = { }
+                )
+            }
+        }
+
+        rule.onNode(isToggleable())
+            .performMouseInput { enter(center) }
+
+        rule.waitForIdle()
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_hover")
+    }
+
+    @Test
+    fun switchTest_focus() {
+        val focusRequester = FocusRequester()
+        var localInputModeManager: InputModeManager? = null
+
+        rule.setMaterialContent(scheme.colorScheme) {
+            localInputModeManager = LocalInputModeManager.current
+            Box(wrapperModifier) {
+                Switch(
+                    checked = true,
+                    onCheckedChange = { },
+                    modifier = Modifier
+                        .testTag("switch")
+                        .focusRequester(focusRequester)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            localInputModeManager!!.requestInputMode(InputMode.Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        rule.waitForIdle()
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_focus")
+    }
+
+    @Test
+    fun switchTest_checked_icon() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            val icon: @Composable () -> Unit = {
+                Icon(
+                    imageVector = Icons.Filled.Check,
+                    contentDescription = null,
+                    modifier = Modifier.size(SwitchDefaults.IconSize),
+                )
+            }
+            Box(wrapperModifier) {
+                Switch(
+                    checked = true,
+                    onCheckedChange = { },
+                    thumbContent = icon
+                )
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_checked_icon")
+    }
+
+    @Test
+    fun switchTest_unchecked_icon() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            val icon: @Composable () -> Unit = {
+                Icon(
+                    imageVector = Icons.Filled.Close,
+                    contentDescription = null,
+                    modifier = Modifier.size(SwitchDefaults.IconSize),
+                )
+            }
+            Box(wrapperModifier) {
+                Switch(
+                    checked = false,
+                    onCheckedChange = { },
+                    thumbContent = icon
+                )
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_${scheme.name}_unchecked_icon")
+    }
+
+    private fun assertToggeableAgainstGolden(goldenName: String) {
+        rule.onNodeWithTag(wrapperTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenName)
+    }
+
+    // Provide the ColorScheme and their name parameter in a ColorSchemeWrapper.
+    // This makes sure that the default method name and the initial Scuba image generated
+    // name is as expected.
+    companion object {
+        @OptIn(ExperimentalTvMaterial3Api::class)
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = arrayOf(
+            ColorSchemeWrapper("lightTheme", lightColorScheme()),
+            ColorSchemeWrapper("darkTheme", darkColorScheme()),
+        )
+    }
+
+    @OptIn(ExperimentalTvMaterial3Api::class)
+    class ColorSchemeWrapper constructor(val name: String, val colorScheme: ColorScheme) {
+        override fun toString(): String {
+            return name
+        }
+    }
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchTest.kt
new file mode 100644
index 0000000..fbe4bb2
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SwitchTest.kt
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveableStateHolder
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.focused
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.isFocusable
+import androidx.compose.ui.test.isNotFocusable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performKeyInput
+import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalTestApi::class, ExperimentalTvMaterial3Api::class)
+class SwitchTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val defaultSwitchTag = "switch"
+
+    @Test
+    fun switch_defaultSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Column {
+                Switch(
+                    modifier = Modifier.testTag("checked"),
+                    checked = true,
+                    onCheckedChange = {})
+                Switch(
+                    modifier = Modifier.testTag("unchecked"),
+                    checked = false,
+                    onCheckedChange = {}
+                )
+            }
+        }
+
+        rule.onNodeWithTag("checked")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Switch))
+            .assertIsEnabled()
+            .assertIsOn()
+        rule.onNodeWithTag("unchecked")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Switch))
+            .assertIsEnabled()
+            .assertIsOff()
+    }
+
+    @Test
+    fun switch_toggle() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val (checked, onChecked) = remember { mutableStateOf(false) }
+
+            // Box is needed because otherwise the control will be expanded to fill its parent
+            Box {
+                Switch(
+                    modifier = Modifier.testTag(defaultSwitchTag),
+                    checked = checked,
+                    onCheckedChange = onChecked
+                )
+            }
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun switch_toggleTwice() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val (checked, onChecked) = remember { mutableStateOf(false) }
+
+            // Box is needed because otherwise the control will be expanded to fill its parent
+            Box {
+                Switch(
+                    modifier = Modifier.testTag(defaultSwitchTag),
+                    checked = checked,
+                    onCheckedChange = onChecked
+                )
+            }
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @Test
+    fun switch_uncheckableWithNoLambda() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val (checked, _) = remember { mutableStateOf(false) }
+            Switch(
+                modifier = Modifier.testTag(defaultSwitchTag),
+                checked = checked,
+                onCheckedChange = {},
+                enabled = false
+            )
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun switch_untoggleable_whenEmptyLambda() {
+        val parentTag = "parent"
+        rule.setMaterialContent(lightColorScheme()) {
+            val (checked, _) = remember { mutableStateOf(false) }
+            Box(
+                Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(parentTag)) {
+                Switch(
+                    checked,
+                    {},
+                    enabled = false,
+                    modifier = Modifier
+                        .testTag(defaultSwitchTag)
+                        .semantics { focused = true }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .assertHasClickAction()
+
+        // Check not merged into parent
+        rule.onNodeWithTag(parentTag)
+            .assert(isNotFocusable())
+    }
+
+    @Test
+    fun switch_untoggleableAndMergeable_whenNullLambda() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val (checked, _) = remember { mutableStateOf(false) }
+            Box(
+                Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(defaultSwitchTag)) {
+                Switch(
+                    checked,
+                    null,
+                    modifier = Modifier.semantics { focused = true }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .assertHasNoClickAction()
+            .assert(isFocusable()) // Check merged into parent
+    }
+
+    @Test
+    fun switch_stateChange_movesThumb() {
+        var checked by mutableStateOf(false)
+        rule.setMaterialContent(lightColorScheme()) {
+            val spacer = @Composable {
+                Spacer(
+                    Modifier
+                        .size(16.dp)
+                        .testTag("spacer")
+                )
+            }
+            Switch(
+                modifier = Modifier.testTag(defaultSwitchTag),
+                checked = checked,
+                thumbContent = spacer,
+                onCheckedChange = { checked = it },
+            )
+        }
+
+        rule.onNodeWithTag("spacer", useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp)
+
+        rule.runOnIdle { checked = true }
+
+        rule.onNodeWithTag("spacer", useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(28.dp)
+
+        rule.runOnIdle { checked = false }
+
+        rule.onNodeWithTag("spacer", useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp)
+    }
+
+    @Test
+    fun switch_constantState_doesNotAnimate() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val spacer = @Composable {
+                Spacer(
+                    Modifier
+                        .size(16.dp)
+                        .testTag("spacer")
+                )
+            }
+            Switch(
+                modifier = Modifier.testTag(defaultSwitchTag),
+                checked = false,
+                thumbContent = spacer,
+                onCheckedChange = {},
+            )
+        }
+
+        rule.onNodeWithTag(defaultSwitchTag)
+            .performKeyInput {
+                pressKey(Key.DirectionCenter)
+            }
+
+        rule.onNodeWithTag("spacer", useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp)
+    }
+
+    // regression test for b/191375128
+    @Test
+    fun switch_stateRestoration_stateChangeWhileSaved() {
+        val screenTwo = mutableStateOf(false)
+        var items by mutableStateOf(listOf(1 to false, 2 to true))
+        rule.setMaterialContent(lightColorScheme()) {
+            Column {
+                Button(onClick = { screenTwo.value = !screenTwo.value }) {
+                    Text("switch screen")
+                }
+                val holder = rememberSaveableStateHolder()
+                holder.SaveableStateProvider(screenTwo.value) {
+                    if (screenTwo.value) {
+                        // second screen, just some random content
+                        Text("Second screen")
+                    } else {
+                        Column {
+                            Text("screen one")
+                            items.forEachIndexed { index, item ->
+                                Row {
+                                    Text("Item ${item.first}")
+                                    Switch(
+                                        modifier = Modifier.testTag(item.first.toString()),
+                                        checked = item.second,
+                                        onCheckedChange = {
+                                            items = items.toMutableList().also {
+                                                it[index] = item.first to !item.second
+                                            }
+                                        }
+                                    )
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1").assertIsOff()
+        rule.onNodeWithTag("2").assertIsOn()
+        rule.runOnIdle {
+            screenTwo.value = true
+        }
+        rule.runOnIdle {
+            items = items.toMutableList().also {
+                it[0] = items[0].first to !items[0].second
+                it[1] = items[1].first to !items[1].second
+            }
+        }
+        rule.runOnIdle {
+            screenTwo.value = false
+        }
+        rule.onNodeWithTag("1").assertIsOn()
+        rule.onNodeWithTag("2").assertIsOff()
+    }
+}
\ No newline at end of file
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 50602d5..c8b28a0 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
@@ -16,9 +16,6 @@
 
 package androidx.tv.material3
 
-import android.view.KeyEvent.KEYCODE_BACK
-import android.view.KeyEvent.KEYCODE_DPAD_LEFT
-import android.view.KeyEvent.KEYCODE_DPAD_RIGHT
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibilityScope
 import androidx.compose.animation.ContentTransform
@@ -49,19 +46,25 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.FocusDirection.Companion.Left
+import androidx.compose.ui.focus.FocusDirection.Companion.Right
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.focusProperties
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.KeyEventType.Companion.KeyUp
 import androidx.compose.ui.input.key.key
-import androidx.compose.ui.input.key.nativeKeyCode
 import androidx.compose.ui.input.key.onKeyEvent
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -127,11 +130,13 @@
         onAutoScrollChange = { isAutoScrollActive = it })
 
     Box(modifier = modifier
+        .semantics {
+            collectionInfo = CollectionInfo(rowCount = 1, columnCount = itemCount)
+        }
         .bringIntoViewIfChildrenAreFocused()
         .focusRequester(carouselOuterBoxFocusRequester)
         .onFocusChanged {
             focusState = it
-
             // When the carousel gains focus for the first time
             if (it.isFocused && isAutoScrollActive) {
                 focusManager.moveFocus(FocusDirection.Enter)
@@ -142,8 +147,8 @@
             outerBoxFocusRequester = carouselOuterBoxFocusRequester,
             focusManager = focusManager,
             itemCount = itemCount,
-            isLtr = isLtr,
-        )
+            isLtr = isLtr
+        ) { focusState }
         .focusable()
     ) {
         AnimatedContent(
@@ -154,7 +159,8 @@
                 } else {
                     contentTransformStartToEnd
                 }
-            }
+            },
+            label = "CarouselAnimation"
         ) { activeItemIndex ->
             LaunchedEffect(Unit) {
                 [email protected] {
@@ -226,69 +232,93 @@
     outerBoxFocusRequester: FocusRequester,
     focusManager: FocusManager,
     itemCount: Int,
-    isLtr: Boolean
+    isLtr: Boolean,
+    currentCarouselBoxFocusState: () -> FocusState?
 ): Modifier = onKeyEvent {
-    // Ignore KeyUp action type
-    if (it.type == KeyUp) {
-        return@onKeyEvent KeyEventPropagation.ContinuePropagation
+    fun showPreviousItem() {
+        carouselState.moveToPreviousItem(itemCount)
+        outerBoxFocusRequester.requestFocus()
+    }
+    fun showNextItem() {
+        carouselState.moveToNextItem(itemCount)
+        outerBoxFocusRequester.requestFocus()
     }
 
-    val showPreviousItemAndGetKeyEventPropagation = {
-        if (carouselState.isFirstItem()) {
-            KeyEventPropagation.ContinuePropagation
-        } else {
-            carouselState.moveToPreviousItem(itemCount)
-            outerBoxFocusRequester.requestFocus()
-            KeyEventPropagation.StopPropagation
-        }
-    }
-    val showNextItemAndGetKeyEventPropagation = {
-        if (carouselState.isLastItem(itemCount)) {
-            KeyEventPropagation.ContinuePropagation
-        } else {
-            carouselState.moveToNextItem(itemCount)
-            outerBoxFocusRequester.requestFocus()
-            KeyEventPropagation.StopPropagation
+    fun updateItemBasedOnLayout(direction: FocusDirection, isLtr: Boolean) {
+        when (direction) {
+            Left -> if (isLtr) showPreviousItem() else showNextItem()
+            Right -> if (isLtr) showNextItem() else showPreviousItem()
         }
     }
 
-    when (it.key.nativeKeyCode) {
-        KEYCODE_BACK -> {
+    fun handledHorizontalFocusMove(direction: FocusDirection): Boolean =
+        when {
+            it.nativeKeyEvent.repeatCount > 0 ->
+                // Ignore long press key event for manual scrolling
+                KeyEventPropagation.StopPropagation
+
+            currentCarouselBoxFocusState()?.isFocused == true ->
+                // if carousel box has focus, do not trigger focus search as it can cause focus to
+                // move out of Carousel unintentionally.
+                if (shouldFocusExitCarousel(direction, carouselState, itemCount, isLtr)) {
+                    KeyEventPropagation.ContinuePropagation
+                } else {
+                    updateItemBasedOnLayout(direction, isLtr)
+                    KeyEventPropagation.StopPropagation
+                }
+
+            !focusManager.moveFocus(direction) -> {
+                // if focus search was unsuccessful, interpret as input for slide change
+                updateItemBasedOnLayout(direction, isLtr)
+                KeyEventPropagation.StopPropagation
+            }
+            else -> KeyEventPropagation.StopPropagation
+        }
+
+    when {
+        // Ignore KeyUp action type
+        it.type == KeyUp -> KeyEventPropagation.ContinuePropagation
+        it.key == Key.Back -> {
             focusManager.moveFocus(FocusDirection.Exit)
             KeyEventPropagation.ContinuePropagation
         }
 
-        KEYCODE_DPAD_LEFT -> {
-            // Ignore long press key event for manual scrolling
-            if (it.nativeKeyEvent.repeatCount > 0) {
-                return@onKeyEvent KeyEventPropagation.StopPropagation
-            }
-
-            if (isLtr) {
-                showPreviousItemAndGetKeyEventPropagation()
-            } else {
-                showNextItemAndGetKeyEventPropagation()
-            }
-        }
-
-        KEYCODE_DPAD_RIGHT -> {
-            // Ignore long press key event for manual scrolling
-            if (it.nativeKeyEvent.repeatCount > 0) {
-                return@onKeyEvent KeyEventPropagation.StopPropagation
-            }
-
-            if (isLtr) {
-                showNextItemAndGetKeyEventPropagation()
-            } else {
-                showPreviousItemAndGetKeyEventPropagation()
-            }
-        }
+        it.key == Key.DirectionLeft -> handledHorizontalFocusMove(Left)
+        it.key == Key.DirectionRight -> handledHorizontalFocusMove(Right)
 
         else -> KeyEventPropagation.ContinuePropagation
     }
+}.focusProperties {
+    // allow exit along horizontal axis only for first and last slide.
+    exit = {
+        when {
+            shouldFocusExitCarousel(it, carouselState, itemCount, isLtr) ->
+                FocusRequester.Default
+            else -> FocusRequester.Cancel
+        }
+    }
 }
 
 @OptIn(ExperimentalTvMaterial3Api::class)
+private fun shouldFocusExitCarousel(
+    focusDirection: FocusDirection,
+    carouselState: CarouselState,
+    itemCount: Int,
+    isLtr: Boolean
+): Boolean =
+    when {
+        // LTR: Don't exit if not first item
+        focusDirection == Left && isLtr && !carouselState.isFirstItem() -> false
+        // RTL: Don't exit if it is not the last item
+        focusDirection == Left && !isLtr && !carouselState.isLastItem(itemCount) -> false
+        // LTR: Don't exit if not last item
+        focusDirection == Right && isLtr && !carouselState.isLastItem(itemCount) -> false
+        // RTL: Don't exit if it is not the first item
+        focusDirection == Right && !isLtr && !carouselState.isFirstItem() -> false
+        else -> true
+    }
+
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun CarouselStateUpdater(carouselState: CarouselState, itemCount: Int) {
     LaunchedEffect(carouselState, itemCount) {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
index 2996e8d..bb10fd9 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
@@ -16,12 +16,15 @@
 
 package androidx.tv.material3
 
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.ContentTransform
 import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.slideInHorizontally
 import androidx.compose.animation.slideOutHorizontally
 import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
@@ -37,8 +40,13 @@
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
 
@@ -67,6 +75,10 @@
         CarouselItemDefaults.contentTransformStartToEnd,
     content: @Composable () -> Unit,
 ) {
+    val context = LocalContext.current
+    val accessibilityManager = remember {
+        context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+    }
     var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
     val focusManager = LocalFocusManager.current
     var exitFocus by remember { mutableStateOf(false) }
@@ -81,6 +93,16 @@
     // This box holds the focus until the overlay animation completes
     Box(
         modifier = modifier
+            .semantics(mergeDescendants = true) {
+                isContainer = true
+                collectionItemInfo =
+                    CollectionItemInfo(
+                        rowIndex = 0,
+                        rowSpan = 1,
+                        columnIndex = itemIndex,
+                        columnSpan = 1
+                    )
+            }
             .onKeyEvent {
                 exitFocus = it.isBackPress() && it.isTypeKeyDown()
                 ContinuePropagation
@@ -92,7 +114,14 @@
                     exitFocus = false
                 }
             }
-            .focusable()
+            .then(
+                if (accessibilityManager.isEnabled)
+                    Modifier.clickable {
+                        focusManager.moveFocus(FocusDirection.Enter)
+                    }
+                else
+                    Modifier.focusable()
+            )
     ) {
         background()
 
@@ -102,7 +131,10 @@
             exit = contentTransform.initialContentExit,
         ) {
             LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
-                if (!transition.isRunning && containerBoxFocusState?.isFocused == true) {
+                if (!transition.isRunning &&
+                    containerBoxFocusState?.isFocused == true &&
+                    !accessibilityManager.isEnabled
+                ) {
                     focusManager.moveFocus(FocusDirection.Enter)
                 }
             }
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
new file mode 100644
index 0000000..33e5a85
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/RadioButton.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.tokens.RadioButtonTokens
+
+/**
+ * <a href="https://m3.material.io/components/radio-button/overview" class="external" target="_blank">Material Design radio button</a>.
+ *
+ * Radio buttons allow users to select one option from a set.
+ *
+ * ![Radio button image](https://developer.android.com/images/reference/androidx/compose/material3/radio-button.png)
+ *
+ * @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.
+ * @param modifier the [Modifier] to be applied to this radio button
+ * @param enabled controls the enabled state of this radio button. When `false`, this component will
+ * not respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [RadioButtonColors] that will be used to resolve the color used for this radio
+ * button in different states. See [RadioButtonDefaults.colors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this radio button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this radio button in different states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun RadioButton(
+    selected: Boolean,
+    onClick: (() -> Unit)?,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: RadioButtonColors = RadioButtonDefaults.colors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+    val dotRadius = animateDpAsState(
+        targetValue = if (selected) RadioButtonDotSize / 2 else 0.dp,
+        animationSpec = tween(durationMillis = RadioAnimationDuration)
+    )
+    val radioColor = colors.radioColor(enabled, selected)
+    val selectableModifier =
+        if (onClick != null) {
+            Modifier.selectable(
+                selected = selected,
+                onClick = onClick,
+                enabled = enabled,
+                role = Role.RadioButton,
+                interactionSource = interactionSource,
+                indication = null
+            )
+        } else {
+            Modifier
+        }
+    Canvas(
+        modifier
+            .then(selectableModifier)
+            .wrapContentSize(Alignment.Center)
+            .padding(RadioButtonPadding)
+            .requiredSize(RadioButtonTokens.IconSize)
+    ) {
+        // Draw the radio button
+        val strokeWidth = RadioStrokeWidth.toPx()
+        drawCircle(
+            radioColor.value,
+            radius = (RadioButtonTokens.IconSize / 2).toPx() - strokeWidth / 2,
+            style = Stroke(strokeWidth)
+        )
+        if (dotRadius.value > 0.dp) {
+            drawCircle(radioColor.value, dotRadius.value.toPx() - strokeWidth / 2, style = Fill)
+        }
+    }
+}
+
+/**
+ * Defaults used in [RadioButton].
+ */
+@ExperimentalTvMaterial3Api
+object RadioButtonDefaults {
+    /**
+     * Creates a [RadioButtonColors] that will animate between the provided colors according to
+     * the Material specification.
+     *
+     * @param selectedColor the color to use for the RadioButton when selected and enabled.
+     * @param unselectedColor the color to use for the RadioButton when unselected and enabled.
+     * @param disabledSelectedColor the color to use for the RadioButton when disabled and selected.
+     * @param disabledUnselectedColor the color to use for the RadioButton when disabled and not
+     * selected.
+     * @return the resulting [RadioButtonColors] used for the RadioButton
+     */
+    @Composable
+    fun colors(
+        selectedColor: Color = RadioButtonTokens.SelectedIconColor.toColor(),
+        unselectedColor: Color = RadioButtonTokens.UnselectedIconColor.toColor(),
+        disabledSelectedColor: Color = RadioButtonTokens.DisabledSelectedIconColor
+            .toColor()
+            .copy(alpha = RadioButtonTokens.DisabledSelectedIconOpacity),
+        disabledUnselectedColor: Color = RadioButtonTokens.DisabledUnselectedIconColor
+            .toColor()
+            .copy(alpha = RadioButtonTokens.DisabledUnselectedIconOpacity)
+    ): RadioButtonColors = RadioButtonColors(
+        selectedColor,
+        unselectedColor,
+        disabledSelectedColor,
+        disabledUnselectedColor
+    )
+}
+
+/**
+ * Represents the color used by a [RadioButton] in different states.
+ *
+ * See [RadioButtonDefaults.colors] for the default implementation that follows Material
+ * specifications.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class RadioButtonColors internal constructor(
+    private val selectedColor: Color,
+    private val unselectedColor: Color,
+    private val disabledSelectedColor: Color,
+    private val disabledUnselectedColor: Color
+) {
+    /**
+     * Represents the main color used to draw the outer and inner circles, depending on whether
+     * the [RadioButton] is [enabled] / [selected].
+     *
+     * @param enabled whether the [RadioButton] is enabled
+     * @param selected whether the [RadioButton] is selected
+     */
+    @Composable
+    internal fun radioColor(enabled: Boolean, selected: Boolean): State<Color> {
+        val target = when {
+            enabled && selected -> selectedColor
+            enabled && !selected -> unselectedColor
+            !enabled && selected -> disabledSelectedColor
+            else -> disabledUnselectedColor
+        }
+
+        // If not enabled 'snap' to the disabled state, as there should be no animations between
+        // enabled / disabled.
+        return if (enabled) {
+            animateColorAsState(target, tween(durationMillis = RadioAnimationDuration))
+        } else {
+            rememberUpdatedState(target)
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is RadioButtonColors) return false
+
+        if (selectedColor != other.selectedColor) return false
+        if (unselectedColor != other.unselectedColor) return false
+        if (disabledSelectedColor != other.disabledSelectedColor) return false
+        if (disabledUnselectedColor != other.disabledUnselectedColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = selectedColor.hashCode()
+        result = 31 * result + unselectedColor.hashCode()
+        result = 31 * result + disabledSelectedColor.hashCode()
+        result = 31 * result + disabledUnselectedColor.hashCode()
+        return result
+    }
+}
+
+private const val RadioAnimationDuration = 100
+
+private val RadioButtonPadding = 2.dp
+private val RadioButtonDotSize = 12.dp
+private val RadioStrokeWidth = 2.dp
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
new file mode 100644
index 0000000..936056a
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Switch.kt
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.tokens.SwitchTokens
+import kotlin.math.roundToInt
+import kotlinx.coroutines.launch
+
+/**
+ * <a href="https://m3.material.io/components/switch" class="external" target="_blank">Material Design Switch</a>.
+ *
+ * Switches toggle the state of a single item on or off.
+ *
+ * ![Switch image](https://developer.android.com/images/reference/androidx/compose/material3/switch.png)
+ *
+ * Switch can be used with a custom icon via [thumbContent] parameter
+ *
+ * @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.
+ * @param modifier the [Modifier] to be applied to this switch
+ * @param thumbContent content that will be drawn inside the thumb, expected to measure
+ * [SwitchDefaults.IconSize]
+ * @param enabled controls the enabled state of this switch. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [SwitchColors] that will be used to resolve the colors used for this switch in
+ * different states. See [SwitchDefaults.colors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this switch. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this switch in different states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+@Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
+fun Switch(
+    checked: Boolean,
+    onCheckedChange: ((Boolean) -> Unit)?,
+    modifier: Modifier = Modifier,
+    thumbContent: (@Composable () -> Unit)? = null,
+    enabled: Boolean = true,
+    colors: SwitchColors = SwitchDefaults.colors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) {
+    val uncheckedThumbDiameter = if (thumbContent == null) {
+        UncheckedThumbDiameter
+    } else {
+        ThumbDiameter
+    }
+
+    val thumbPaddingStart = (SwitchHeight - uncheckedThumbDiameter) / 2
+    val minBound = with(LocalDensity.current) { thumbPaddingStart.toPx() }
+    val maxBound = with(LocalDensity.current) { ThumbPathLength.toPx() }
+    val valueToOffset = remember<(Boolean) -> Float>(minBound, maxBound) {
+        { value -> if (value) maxBound else minBound }
+    }
+
+    val targetValue = valueToOffset(checked)
+    val offset = remember { Animatable(targetValue) }
+    val scope = rememberCoroutineScope()
+
+    SideEffect {
+        // min bound might have changed if the icon is only rendered in checked state.
+        offset.updateBounds(lowerBound = minBound)
+    }
+
+    DisposableEffect(checked) {
+        if (offset.targetValue != targetValue) {
+            scope.launch {
+                offset.animateTo(targetValue, AnimationSpec)
+            }
+        }
+        onDispose { }
+    }
+
+    // TODO: Add Swipeable modifier b/223797571
+    val toggleableModifier =
+        if (onCheckedChange != null) {
+            Modifier.toggleable(
+                value = checked,
+                onValueChange = onCheckedChange,
+                enabled = enabled,
+                role = Role.Switch,
+                interactionSource = interactionSource,
+                indication = null
+            )
+        } else {
+            Modifier
+        }
+
+    Box(
+        modifier
+            .then(toggleableModifier)
+            .wrapContentSize(Alignment.Center)
+            .requiredSize(SwitchWidth, SwitchHeight)
+    ) {
+        SwitchImpl(
+            checked = checked,
+            enabled = enabled,
+            colors = colors,
+            thumbValue = offset.asState(),
+            interactionSource = interactionSource,
+            thumbShape = SwitchTokens.HandleShape.toShape(),
+            uncheckedThumbDiameter = uncheckedThumbDiameter,
+            minBound = thumbPaddingStart,
+            maxBound = ThumbPathLength,
+            thumbContent = thumbContent,
+        )
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+@Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
+private fun BoxScope.SwitchImpl(
+    checked: Boolean,
+    enabled: Boolean,
+    colors: SwitchColors,
+    thumbValue: State<Float>,
+    thumbContent: (@Composable () -> Unit)?,
+    interactionSource: InteractionSource,
+    thumbShape: Shape,
+    uncheckedThumbDiameter: Dp,
+    minBound: Dp,
+    maxBound: Dp,
+) {
+    val trackColor by colors.trackColor(enabled, checked)
+    val isPressed by interactionSource.collectIsPressedAsState()
+
+    val thumbValueDp = with(LocalDensity.current) { thumbValue.value.toDp() }
+    val thumbSizeDp = if (isPressed) {
+        SwitchTokens.PressedHandleWidth
+    } else {
+        uncheckedThumbDiameter + (ThumbDiameter - uncheckedThumbDiameter) *
+            ((thumbValueDp - minBound) / (maxBound - minBound))
+    }
+
+    val thumbOffset = if (isPressed) {
+        with(LocalDensity.current) {
+            if (checked) {
+                ThumbPathLength - SwitchTokens.TrackOutlineWidth
+            } else {
+                SwitchTokens.TrackOutlineWidth
+            }.toPx()
+        }
+    } else {
+        thumbValue.value
+    }
+
+    val trackShape = SwitchTokens.TrackShape.toShape()
+    val modifier = Modifier
+        .align(Alignment.Center)
+        .width(SwitchWidth)
+        .height(SwitchHeight)
+        .border(
+            SwitchTokens.TrackOutlineWidth,
+            colors.borderColor(enabled, checked).value,
+            trackShape
+        )
+        .background(trackColor, trackShape)
+
+    Box(modifier) {
+        val thumbColor by colors.thumbColor(enabled, checked)
+        val resolvedThumbColor = thumbColor
+        Box(
+            modifier = Modifier
+                .align(Alignment.CenterStart)
+                .offset { IntOffset(thumbOffset.roundToInt(), 0) }
+                .indication(
+                    interactionSource = interactionSource,
+                    indication = null
+                )
+                .requiredSize(thumbSizeDp)
+                .background(resolvedThumbColor, thumbShape),
+            contentAlignment = Alignment.Center
+        ) {
+            if (thumbContent != null) {
+                val iconColor = colors.iconColor(enabled, checked)
+                CompositionLocalProvider(
+                    LocalContentColor provides iconColor.value,
+                    content = thumbContent
+                )
+            }
+        }
+    }
+}
+
+internal val ThumbDiameter = SwitchTokens.SelectedHandleWidth
+internal val UncheckedThumbDiameter = SwitchTokens.UnselectedHandleWidth
+private val SwitchWidth = SwitchTokens.TrackWidth
+private val SwitchHeight = SwitchTokens.TrackHeight
+private val ThumbPadding = (SwitchHeight - ThumbDiameter) / 2
+private val ThumbPathLength = (SwitchWidth - ThumbDiameter) - ThumbPadding
+
+private val AnimationSpec = TweenSpec<Float>(durationMillis = 100)
+
+/**
+ * Contains the default values used by [Switch]
+ */
+@ExperimentalTvMaterial3Api
+object SwitchDefaults {
+    /**
+     * Creates a [SwitchColors] that represents the different colors used in a [Switch] in
+     * different states.
+     *
+     * @param checkedThumbColor the color used for the thumb when enabled and checked
+     * @param checkedTrackColor the color used for the track when enabled and checked
+     * @param checkedBorderColor the color used for the border when enabled and checked
+     * @param checkedIconColor the color used for the icon when enabled and checked
+     * @param uncheckedThumbColor the color used for the thumb when enabled and unchecked
+     * @param uncheckedTrackColor the color used for the track when enabled and unchecked
+     * @param uncheckedBorderColor the color used for the border when enabled and unchecked
+     * @param uncheckedIconColor the color used for the icon when enabled and unchecked
+     * @param disabledCheckedThumbColor the color used for the thumb when disabled and checked
+     * @param disabledCheckedTrackColor the color used for the track when disabled and checked
+     * @param disabledCheckedBorderColor the color used for the border when disabled and checked
+     * @param disabledCheckedIconColor the color used for the icon when disabled and checked
+     * @param disabledUncheckedThumbColor the color used for the thumb when disabled and unchecked
+     * @param disabledUncheckedTrackColor the color used for the track when disabled and unchecked
+     * @param disabledUncheckedBorderColor the color used for the border when disabled and unchecked
+     * @param disabledUncheckedIconColor the color used for the icon when disabled and unchecked
+     */
+    @Composable
+    fun colors(
+        checkedThumbColor: Color = SwitchTokens.SelectedHandleColor.toColor(),
+        checkedTrackColor: Color = SwitchTokens.SelectedTrackColor.toColor(),
+        checkedBorderColor: Color = Color.Transparent,
+        checkedIconColor: Color = SwitchTokens.SelectedIconColor.toColor(),
+        uncheckedThumbColor: Color = SwitchTokens.UnselectedHandleColor.toColor(),
+        uncheckedTrackColor: Color = SwitchTokens.UnselectedTrackColor.toColor(),
+        uncheckedBorderColor: Color = SwitchTokens.UnselectedFocusTrackOutlineColor.toColor(),
+        uncheckedIconColor: Color = SwitchTokens.UnselectedIconColor.toColor(),
+        disabledCheckedThumbColor: Color = SwitchTokens.DisabledSelectedHandleColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledSelectedHandleOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledCheckedTrackColor: Color = SwitchTokens.DisabledSelectedTrackColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledTrackOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledCheckedBorderColor: Color = Color.Transparent,
+        disabledCheckedIconColor: Color = SwitchTokens.DisabledSelectedIconColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledSelectedIconOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedThumbColor: Color = SwitchTokens.DisabledUnselectedHandleColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledUnselectedHandleOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedTrackColor: Color = SwitchTokens.DisabledUnselectedTrackColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledTrackOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedBorderColor: Color =
+            SwitchTokens.DisabledUnselectedTrackOutlineColor.toColor()
+                .copy(alpha = SwitchTokens.DisabledTrackOpacity)
+                .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedIconColor: Color = SwitchTokens.DisabledUnselectedIconColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledUnselectedIconOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+    ): SwitchColors = SwitchColors(
+        checkedThumbColor = checkedThumbColor,
+        checkedTrackColor = checkedTrackColor,
+        checkedBorderColor = checkedBorderColor,
+        checkedIconColor = checkedIconColor,
+        uncheckedThumbColor = uncheckedThumbColor,
+        uncheckedTrackColor = uncheckedTrackColor,
+        uncheckedBorderColor = uncheckedBorderColor,
+        uncheckedIconColor = uncheckedIconColor,
+        disabledCheckedThumbColor = disabledCheckedThumbColor,
+        disabledCheckedTrackColor = disabledCheckedTrackColor,
+        disabledCheckedBorderColor = disabledCheckedBorderColor,
+        disabledCheckedIconColor = disabledCheckedIconColor,
+        disabledUncheckedThumbColor = disabledUncheckedThumbColor,
+        disabledUncheckedTrackColor = disabledUncheckedTrackColor,
+        disabledUncheckedBorderColor = disabledUncheckedBorderColor,
+        disabledUncheckedIconColor = disabledUncheckedIconColor
+    )
+
+    /**
+     * Icon size to use for `thumbContent`
+     */
+    val IconSize = 16.dp
+}
+
+/**
+ * Represents the colors used by a [Switch] in different states
+ *
+ * See [SwitchDefaults.colors] for the default implementation that follows Material
+ * specifications.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class SwitchColors internal constructor(
+    private val checkedThumbColor: Color,
+    private val checkedTrackColor: Color,
+    private val checkedBorderColor: Color,
+    private val checkedIconColor: Color,
+    private val uncheckedThumbColor: Color,
+    private val uncheckedTrackColor: Color,
+    private val uncheckedBorderColor: Color,
+    private val uncheckedIconColor: Color,
+    private val disabledCheckedThumbColor: Color,
+    private val disabledCheckedTrackColor: Color,
+    private val disabledCheckedBorderColor: Color,
+    private val disabledCheckedIconColor: Color,
+    private val disabledUncheckedThumbColor: Color,
+    private val disabledUncheckedTrackColor: Color,
+    private val disabledUncheckedBorderColor: Color,
+    private val disabledUncheckedIconColor: Color
+) {
+    /**
+     * Represents the color used for the switch's thumb, depending on [enabled] and [checked].
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    internal fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedThumbColor else uncheckedThumbColor
+            } else {
+                if (checked) disabledCheckedThumbColor else disabledUncheckedThumbColor
+            }
+        )
+    }
+
+    /**
+     * Represents the color used for the switch's track, depending on [enabled] and [checked].
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    internal fun trackColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedTrackColor else uncheckedTrackColor
+            } else {
+                if (checked) disabledCheckedTrackColor else disabledUncheckedTrackColor
+            }
+        )
+    }
+
+    /**
+     * Represents the color used for the switch's border, depending on [enabled] and [checked].
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    internal fun borderColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedBorderColor else uncheckedBorderColor
+            } else {
+                if (checked) disabledCheckedBorderColor else disabledUncheckedBorderColor
+            }
+        )
+    }
+
+    /**
+     * Represents the content color passed to the icon if used
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    internal fun iconColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedIconColor else uncheckedIconColor
+            } else {
+                if (checked) disabledCheckedIconColor else disabledUncheckedIconColor
+            }
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is SwitchColors) return false
+
+        if (checkedThumbColor != other.checkedThumbColor) return false
+        if (checkedTrackColor != other.checkedTrackColor) return false
+        if (checkedBorderColor != other.checkedBorderColor) return false
+        if (checkedIconColor != other.checkedIconColor) return false
+        if (uncheckedThumbColor != other.uncheckedThumbColor) return false
+        if (uncheckedTrackColor != other.uncheckedTrackColor) return false
+        if (uncheckedBorderColor != other.uncheckedBorderColor) return false
+        if (uncheckedIconColor != other.uncheckedIconColor) return false
+        if (disabledCheckedThumbColor != other.disabledCheckedThumbColor) return false
+        if (disabledCheckedTrackColor != other.disabledCheckedTrackColor) return false
+        if (disabledCheckedBorderColor != other.disabledCheckedBorderColor) return false
+        if (disabledCheckedIconColor != other.disabledCheckedIconColor) return false
+        if (disabledUncheckedThumbColor != other.disabledUncheckedThumbColor) return false
+        if (disabledUncheckedTrackColor != other.disabledUncheckedTrackColor) return false
+        if (disabledUncheckedBorderColor != other.disabledUncheckedBorderColor) return false
+        if (disabledUncheckedIconColor != other.disabledUncheckedIconColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedThumbColor.hashCode()
+        result = 31 * result + checkedTrackColor.hashCode()
+        result = 31 * result + checkedBorderColor.hashCode()
+        result = 31 * result + checkedIconColor.hashCode()
+        result = 31 * result + uncheckedThumbColor.hashCode()
+        result = 31 * result + uncheckedTrackColor.hashCode()
+        result = 31 * result + uncheckedBorderColor.hashCode()
+        result = 31 * result + uncheckedIconColor.hashCode()
+        result = 31 * result + disabledCheckedThumbColor.hashCode()
+        result = 31 * result + disabledCheckedTrackColor.hashCode()
+        result = 31 * result + disabledCheckedBorderColor.hashCode()
+        result = 31 * result + disabledCheckedIconColor.hashCode()
+        result = 31 * result + disabledUncheckedThumbColor.hashCode()
+        result = 31 * result + disabledUncheckedTrackColor.hashCode()
+        result = 31 * result + disabledUncheckedBorderColor.hashCode()
+        result = 31 * result + disabledUncheckedIconColor.hashCode()
+        return result
+    }
+}
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
index 78a455a..15b30eb 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
@@ -115,25 +115,21 @@
             LocalContentColor.current
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
-            color = textColor,
-            fontSize = fontSize,
-            fontWeight = fontWeight,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            fontFamily = fontFamily,
-            textDecoration = textDecoration,
-            fontStyle = fontStyle,
-            letterSpacing = letterSpacing
-        )
-    )
+
     BasicText(
         text,
         modifier,
-        mergedStyle,
+        style.merge(
+                color = textColor,
+                fontSize = fontSize,
+                fontWeight = fontWeight,
+                textAlign = textAlign,
+                lineHeight = lineHeight,
+                fontFamily = fontFamily,
+                textDecoration = textDecoration,
+                fontStyle = fontStyle,
+                letterSpacing = letterSpacing
+            ),
         onTextLayout,
         overflow,
         softWrap,
@@ -220,25 +216,21 @@
             LocalContentColor.current
         }
     }
-    // NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
-    // will avoid reallocating if all of the options here are the defaults
-    val mergedStyle = style.merge(
-        TextStyle(
-            color = textColor,
-            fontSize = fontSize,
-            fontWeight = fontWeight,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            fontFamily = fontFamily,
-            textDecoration = textDecoration,
-            fontStyle = fontStyle,
-            letterSpacing = letterSpacing
-        )
-    )
+
     BasicText(
         text = text,
         modifier = modifier,
-        style = mergedStyle,
+        style = style.merge(
+                color = textColor,
+                fontSize = fontSize,
+                fontWeight = fontWeight,
+                textAlign = textAlign,
+                lineHeight = lineHeight,
+                fontFamily = fontFamily,
+                textDecoration = textDecoration,
+                fontStyle = fontStyle,
+                letterSpacing = letterSpacing
+        ),
         onTextLayout = onTextLayout,
         overflow = overflow,
         softWrap = softWrap,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/tokens/RadioButtonTokens.kt b/tv/tv-material/src/main/java/androidx/tv/material3/tokens/RadioButtonTokens.kt
new file mode 100644
index 0000000..fab1c59
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/tokens/RadioButtonTokens.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+// VERSION: v0_001 (Inspired by androidx.compose.material3.tokens v0_103)
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.tv.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object RadioButtonTokens {
+    val DisabledSelectedIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledSelectedIconOpacity = 0.38f
+    val DisabledUnselectedIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledUnselectedIconOpacity = 0.38f
+    val IconSize = 20.0.dp
+    val SelectedFocusIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedHoverIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedPressedIconColor = ColorSchemeKeyTokens.Primary
+    val StateLayerSize = 40.0.dp
+    val UnselectedFocusIconColor = ColorSchemeKeyTokens.OnSurface
+    val UnselectedHoverIconColor = ColorSchemeKeyTokens.OnSurface
+    val UnselectedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedIconColor = ColorSchemeKeyTokens.OnSurface
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/tokens/SwitchTokens.kt b/tv/tv-material/src/main/java/androidx/tv/material3/tokens/SwitchTokens.kt
new file mode 100644
index 0000000..498364f
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/tokens/SwitchTokens.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+// VERSION: v0_001 (Inspired by androidx.compose.material3.tokens v0_103)
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.tv.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object SwitchTokens {
+    val DisabledSelectedHandleColor = ColorSchemeKeyTokens.Surface
+    const val DisabledSelectedHandleOpacity = 1.0f
+    val DisabledSelectedIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledSelectedIconOpacity = 0.38f
+    val DisabledSelectedTrackColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledTrackOpacity = 0.12f
+    val DisabledUnselectedHandleColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledUnselectedHandleOpacity = 0.38f
+    val DisabledUnselectedIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    const val DisabledUnselectedIconOpacity = 0.38f
+    val DisabledUnselectedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val DisabledUnselectedTrackOutlineColor = ColorSchemeKeyTokens.OnSurface
+    val HandleShape = ShapeKeyTokens.CornerFull
+    val PressedHandleHeight = 28.0.dp
+    val PressedHandleWidth = 28.0.dp
+    val SelectedFocusHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedFocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedFocusTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedHandleColor = ColorSchemeKeyTokens.OnPrimary
+    val SelectedHandleHeight = 24.0.dp
+    val SelectedHandleWidth = 24.0.dp
+    val SelectedHoverHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedHoverIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedHoverTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedIconSize = 16.0.dp
+    val SelectedPressedHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedPressedIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedPressedTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedTrackColor = ColorSchemeKeyTokens.Primary
+    val StateLayerShape = ShapeKeyTokens.CornerFull
+    val StateLayerSize = 40.0.dp
+    val TrackHeight = 32.0.dp
+    val TrackOutlineWidth = 2.0.dp
+    val TrackShape = ShapeKeyTokens.CornerFull
+    val TrackWidth = 52.0.dp
+    val UnselectedFocusHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedFocusIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedFocusTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedFocusTrackOutlineColor = ColorSchemeKeyTokens.Border
+    val UnselectedHandleColor = ColorSchemeKeyTokens.Border
+    val UnselectedHandleHeight = 16.0.dp
+    val UnselectedHandleWidth = 16.0.dp
+    val UnselectedHoverHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedHoverTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedHoverTrackOutlineColor = ColorSchemeKeyTokens.Border
+    val UnselectedIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedIconSize = 16.0.dp
+    val UnselectedPressedHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedPressedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedPressedTrackOutlineColor = ColorSchemeKeyTokens.Border
+    val UnselectedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedTrackOutlineColor = ColorSchemeKeyTokens.Border
+    val IconHandleHeight = 24.0.dp
+    val IconHandleWidth = 24.0.dp
+}
\ No newline at end of file
diff --git a/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl b/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
index 5b1fea6..8d22e67 100644
--- a/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
+++ b/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
@@ -1,3 +1,18 @@
+/**
+ * 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.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/wear/compose/compose-foundation/api/public_plus_experimental_current.txt b/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
index cf3e0b0..81af87e 100644
--- a/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
@@ -205,6 +205,11 @@
     method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
   }
 
+  @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public interface RevealScope {
+    method public float getRevealOffset();
+    property public abstract float revealOffset;
+  }
+
   @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public final class RevealState {
     method public suspend Object? animateTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public int getCurrentValue();
@@ -236,7 +241,7 @@
   }
 
   public final class SwipeToRevealKt {
-    method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void SwipeToReveal(kotlin.jvm.functions.Function0<kotlin.Unit> action, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onFullSwipe, optional androidx.wear.compose.foundation.RevealState state, optional kotlin.jvm.functions.Function0<kotlin.Unit>? additionalAction, optional kotlin.jvm.functions.Function0<kotlin.Unit>? undoAction, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void SwipeToReveal(kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit> action, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onFullSwipe, optional androidx.wear.compose.foundation.RevealState state, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? additionalAction, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? undoAction, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> createAnchors(optional float coveredAnchor, optional float revealingAnchor, optional float revealedAnchor);
     method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.wear.compose.foundation.RevealState rememberRevealState(optional int initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold, optional java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> anchors);
   }
diff --git a/wear/compose/compose-foundation/samples/build.gradle b/wear/compose/compose-foundation/samples/build.gradle
index be551b8..ea89774 100644
--- a/wear/compose/compose-foundation/samples/build.gradle
+++ b/wear/compose/compose-foundation/samples/build.gradle
@@ -30,6 +30,7 @@
     compileOnly(project(":annotation:annotation-sampled"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
+    implementation(project(":compose:material:material-icons-core"))
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-text"))
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/SwipeToRevealSample.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/SwipeToRevealSample.kt
new file mode 100644
index 0000000..1212135
--- /dev/null
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/SwipeToRevealSample.kt
@@ -0,0 +1,224 @@
+/*
+ * 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.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.MoreVert
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.RevealValue
+import androidx.wear.compose.foundation.SwipeToReveal
+import androidx.wear.compose.foundation.expandableItem
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.rememberExpandableState
+import androidx.wear.compose.foundation.rememberRevealState
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.ListHeader
+import androidx.wear.compose.material.Text
+import kotlin.math.abs
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalWearFoundationApi::class)
+@Sampled
+@Composable
+fun SwipeToRevealSample() {
+    SwipeToReveal(
+        action = {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .clickable { /* Add the primary action */ },
+            ) {
+                Icon(
+                    imageVector = Icons.Outlined.Delete,
+                    contentDescription = "Delete"
+                )
+            }
+        },
+        undoAction = {
+            Chip(
+                modifier = Modifier.fillMaxWidth(),
+                onClick = { /* Add undo action here */ },
+                colors = ChipDefaults.secondaryChipColors(),
+                label = { Text(text = "Undo") }
+            )
+        }
+    ) {
+        Chip(
+            modifier = Modifier.fillMaxWidth(),
+            onClick = { /* the click action associated with chip */ },
+            colors = ChipDefaults.secondaryChipColors(),
+            label = { Text(text = "Swipe Me") }
+        )
+    }
+}
+
+@OptIn(ExperimentalWearFoundationApi::class)
+@Sampled
+@Composable
+fun SwipeToRevealWithRevealOffset() {
+    val state = rememberRevealState()
+    SwipeToReveal(
+        state = state,
+        action = {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .clickable { /* Add the primary action */ },
+            ) {
+                Icon(
+                    imageVector = Icons.Outlined.Delete,
+                    contentDescription = "Delete"
+                )
+                if (abs(state.offset) > revealOffset) {
+                    Spacer(Modifier.size(5.dp))
+                    Text("Clear")
+                }
+            }
+        },
+        undoAction = {
+            Chip(
+                modifier = Modifier.fillMaxWidth(),
+                onClick = { /* Add undo action here */ },
+                colors = ChipDefaults.secondaryChipColors(),
+                label = { Text(text = "Undo") }
+            )
+        }
+    ) {
+        Chip(
+            modifier = Modifier.fillMaxWidth(),
+            onClick = { /* the click action associated with chip */ },
+            colors = ChipDefaults.secondaryChipColors(),
+            label = { Text(text = "Swipe Me") }
+        )
+    }
+}
+
+/**
+ * A sample on how to use Swipe To Reveal within a list of items, preferably [ScalingLazyColumn].
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@Sampled
+@Composable
+fun SwipeToRevealWithExpandables() {
+    // Shape of actions should match with the overlay content. For example, Chips
+    // should use RoundedCornerShape(CornerSize(percent = 50)), Cards should use
+    // RoundedCornerShape with appropriate radius, based on the theme.
+    val actionShape = RoundedCornerShape(corner = CornerSize(percent = 50))
+    val itemCount = 10
+    val coroutineScope = rememberCoroutineScope()
+    val expandableStates = List(itemCount) {
+        rememberExpandableState(initiallyExpanded = true)
+    }
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize()
+    ) {
+        item {
+            ListHeader {
+                Text("Scaling Lazy Column")
+            }
+        }
+        repeat(itemCount) { current ->
+            expandableItem(
+                state = expandableStates[current],
+            ) { isExpanded ->
+                val revealState = rememberRevealState()
+                if (isExpanded) {
+                    SwipeToReveal(
+                        state = revealState,
+                        action = {
+                            Box(
+                                modifier = Modifier
+                                    .fillMaxSize()
+                                    .background(Color.Red, actionShape)
+                                    .clickable {
+                                        coroutineScope.launch {
+                                            revealState.animateTo(RevealValue.Revealed)
+                                        }
+                                    },
+                                contentAlignment = Alignment.Center
+                            ) {
+                                Icon(
+                                    imageVector = Icons.Outlined.Delete,
+                                    contentDescription = "Delete"
+                                )
+                            }
+                        },
+                        additionalAction = {
+                            Box(
+                                modifier = Modifier
+                                    .fillMaxSize()
+                                    .background(Color.Gray, actionShape)
+                                    .clickable { /* trigger the optional action */ },
+                                contentAlignment = Alignment.Center
+                            ) {
+                                Icon(
+                                    imageVector = Icons.Outlined.MoreVert,
+                                    contentDescription = "More Options"
+                                )
+                            }
+                        },
+                        undoAction = {
+                            Chip(
+                                modifier = Modifier.fillMaxWidth(),
+                                onClick = {
+                                    coroutineScope.launch {
+                                        revealState.animateTo(RevealValue.Covered)
+                                    }
+                                },
+                                colors = ChipDefaults.secondaryChipColors(),
+                                label = { Text(text = "Undo") }
+                            )
+                        },
+                        onFullSwipe = {
+                            coroutineScope.launch {
+                                delay(1000)
+                                expandableStates[current].expanded = false
+                            }
+                        }
+                    ) {
+                        Chip(
+                            modifier = Modifier.fillMaxWidth(),
+                            onClick = { /* the click action associated with chip */ },
+                            colors = ChipDefaults.secondaryChipColors(),
+                            label = { Text(text = "Swipe Me") }
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/SwipeToRevealTest.kt b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/SwipeToRevealTest.kt
index 7e65f0e..9f901e2 100644
--- a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/SwipeToRevealTest.kt
+++ b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/SwipeToRevealTest.kt
@@ -138,11 +138,11 @@
 
     @Composable
     private fun swipeToRevealWithDefaults(
-        action: @Composable () -> Unit = { getAction() },
+        action: @Composable RevealScope.() -> Unit = { getAction() },
         state: RevealState = rememberRevealState(),
         modifier: Modifier = Modifier,
-        additionalAction: (@Composable () -> Unit)? = null,
-        undoAction: (@Composable () -> Unit)? = null,
+        additionalAction: (@Composable RevealScope.() -> Unit)? = null,
+        undoAction: (@Composable RevealScope.() -> Unit)? = null,
         content: @Composable () -> Unit = { getBoxContent() }
     ) {
         SwipeToReveal(
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 86e8cb6..50764ab 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,6 +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.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -235,7 +236,14 @@
  * It is strongly recommended to have icons represent the actions and maybe a text and icon for
  * the undo action.
  *
- * TODO: Add Sample
+ * Example of SwipeToReveal with mandatory action and undo action
+ * @sample androidx.wear.compose.foundation.samples.SwipeToRevealSample
+ *
+ * Example of SwipeToReveal using [RevealScope]
+ * @sample androidx.wear.compose.foundation.samples.SwipeToRevealWithRevealOffset
+ *
+ * Example of SwipeToReveal used with Expandables
+ * @sample androidx.wear.compose.foundation.samples.SwipeToRevealWithExpandables
  *
  * @param action The mandatory action that needs to be added to the component.
  * @param modifier Optional [Modifier] for this component.
@@ -251,83 +259,113 @@
 @ExperimentalWearFoundationApi
 @Composable
 public fun SwipeToReveal(
-    action: @Composable () -> Unit,
+    action: @Composable RevealScope.() -> Unit,
     modifier: Modifier = Modifier,
     onFullSwipe: () -> Unit = {},
     state: RevealState = rememberRevealState(),
-    additionalAction: (@Composable () -> Unit)? = null,
-    undoAction: (@Composable () -> Unit)? = null,
+    additionalAction: (@Composable RevealScope.() -> Unit)? = null,
+    undoAction: (@Composable RevealScope.() -> Unit)? = null,
     content: @Composable () -> Unit
-) = Box(
-    modifier = modifier
-        .swipeableV2(
-            state = state.swipeableState,
-            orientation = Orientation.Horizontal,
-            enabled = true,
-        )
-        .swipeAnchors(
-            state = state.swipeableState,
-            possibleValues = state.swipeAnchors.keys
-        ) { value, layoutSize ->
-            val swipeableWidth = layoutSize.width.toFloat()
-            // Multiply the anchor with -1f to get the actual swipeable anchor
-            -state.swipeAnchors[value]!! * swipeableWidth
-        }
 ) {
-    val swipeCompleted by remember {
-        derivedStateOf { state.currentValue == RevealValue.Revealed }
-    }
-    val density = LocalDensity.current
-
-    // Total width available for the slot(s) based on the current swipe offset
-    val availableWidth = if (state.offset.isNaN()) 0.dp
-        else with(density) { abs(state.offset).toDp() }
-    // Total padding between the slots based on the available actions
-    val totalPadding = if (additionalAction != null) SwipeToRevealDefaults.padding * 2
-        else SwipeToRevealDefaults.padding
-
-    Row(
-        modifier = Modifier.matchParentSize(),
-        horizontalArrangement = Arrangement.Absolute.Right
-    ) {
-        if (swipeCompleted && undoAction != null) {
-            Row(
-                modifier = Modifier.fillMaxWidth(),
-                horizontalArrangement = Arrangement.Center
-            ) {
-                ActionSlot(content = undoAction)
-            }
-        } else {
-            Row(
-                modifier = Modifier.width(availableWidth.minus(totalPadding)),
-                horizontalArrangement = Arrangement.Absolute.Right
-            ) {
-                if (additionalAction != null) {
-                    Spacer(Modifier.size(SwipeToRevealDefaults.padding))
-                    ActionSlot(content = additionalAction)
-                }
-                Spacer(Modifier.size(SwipeToRevealDefaults.padding))
-                ActionSlot(content = action)
-            }
-        }
-    }
-    Row(
-        modifier = Modifier.absoluteOffset {
-            IntOffset(
-                x = state.requireOffset().roundToInt().coerceAtMost(0),
-                y = 0
+    // Initialise to zero, updated when the size changes
+    val revealScope = remember(state) { RevealScopeImpl(state) }
+    Box(
+        modifier = modifier
+            .swipeableV2(
+                state = state.swipeableState,
+                orientation = Orientation.Horizontal,
+                enabled = true,
             )
-        }
+            .swipeAnchors(
+                state = state.swipeableState,
+                possibleValues = state.swipeAnchors.keys
+            ) { value, layoutSize ->
+                val swipeableWidth = layoutSize.width.toFloat()
+                // Update the total width which will be used to calculate the anchors
+                revealScope.width.value = swipeableWidth
+                // Multiply the anchor with -1f to get the actual swipeable anchor
+                -state.swipeAnchors[value]!! * swipeableWidth
+            }
     ) {
-        content()
-    }
-    LaunchedEffect(state.currentValue) {
-        if (state.currentValue == RevealValue.Revealed) {
-            onFullSwipe()
+        val swipeCompleted by remember {
+            derivedStateOf { state.currentValue == RevealValue.Revealed }
+        }
+        val density = LocalDensity.current
+
+        // Total width available for the slot(s) based on the current swipe offset
+        val availableWidth = if (state.offset.isNaN()) 0.dp
+        else with(density) { abs(state.offset).toDp() }
+
+        Row(
+            modifier = Modifier.matchParentSize(),
+            horizontalArrangement = Arrangement.Absolute.Right
+        ) {
+            if (swipeCompleted && undoAction != null) {
+                Row(
+                    modifier = Modifier.fillMaxWidth(),
+                    horizontalArrangement = Arrangement.Center
+                ) {
+                    ActionSlot(revealScope, content = undoAction)
+                }
+            } else {
+                Row(
+                    modifier = Modifier.width(availableWidth),
+                    horizontalArrangement = Arrangement.Absolute.Right
+                ) {
+                    if (additionalAction != null &&
+                        abs(state.offset) <= revealScope.revealOffset) {
+                        Spacer(Modifier.size(SwipeToRevealDefaults.padding))
+                        ActionSlot(revealScope, content = additionalAction)
+                    }
+                    Spacer(Modifier.size(SwipeToRevealDefaults.padding))
+                    ActionSlot(revealScope, content = action)
+                }
+            }
+        }
+        Row(
+            modifier = Modifier.absoluteOffset {
+                IntOffset(
+                    x = state.requireOffset().roundToInt().coerceAtMost(0),
+                    y = 0
+                )
+            }
+        ) {
+            content()
+        }
+        LaunchedEffect(state.currentValue) {
+            if (state.currentValue == RevealValue.Revealed) {
+                onFullSwipe()
+            }
         }
     }
 }
 
+@ExperimentalWearFoundationApi
+public interface RevealScope {
+
+    /**
+     * The offset, in pixels, where the revealed actions are fully visible but the existing content
+     * would be left in place if the reveal action was stopped. This offset is used to create the
+     * anchor for [RevealValue.Revealing].
+     * If there is no such anchor defined for [RevealValue.Revealing], it returns 0.0f.
+     */
+    public val revealOffset: Float
+}
+
+@OptIn(ExperimentalWearFoundationApi::class)
+private class RevealScopeImpl constructor(
+    private val revealState: RevealState
+) : RevealScope {
+
+    /**
+     * The total width of the overlay content in float.
+     */
+    val width = mutableStateOf(0.0f)
+
+    override val revealOffset: Float
+        get() = width.value * (revealState.swipeAnchors[RevealValue.Revealing] ?: 0.0f)
+}
+
 /**
  * An internal object containing some defaults used across the Swipe to reveal component.
  */
@@ -343,15 +381,19 @@
     internal fun defaultThreshold() = fractionalPositionalThreshold(threshold)
 }
 
+@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 private fun RowScope.ActionSlot(
+    revealScope: RevealScope,
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit
+    content: @Composable RevealScope.() -> Unit
 ) {
     Box(
         modifier = modifier.fillMaxHeight().weight(1f),
         contentAlignment = Alignment.Center
     ) {
-        content()
+        with(revealScope) {
+            content()
+        }
     }
 }
\ No newline at end of file
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
index 0d57a82..31de3b4 100644
--- a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
@@ -124,6 +124,8 @@
                 scrollBy(value - lastValue)
                 lastValue = value
             }
+            // We consider all velocity consumed
+            return 0.0f
         }
 
         return animationState.velocity
diff --git a/wear/compose/compose-material-core/src/androidAndroidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt b/wear/compose/compose-material-core/src/androidAndroidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
index 32c9176..167a921 100644
--- a/wear/compose/compose-material-core/src/androidAndroidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
+++ b/wear/compose/compose-material-core/src/androidAndroidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
@@ -30,15 +30,12 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.graphics.painter.ColorPainter
-import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
@@ -343,11 +340,9 @@
                     .background(testBackground)
             ) {
                 ButtonWithDefaults(
-                    background = { enabled ->
+                    backgroundColor = { enabled ->
                         rememberUpdatedState(
-                            ColorPainter(
-                                if (enabled) enabledBackgroundColor else disabledBackgroundColor
-                            )
+                            if (enabled) enabledBackgroundColor else disabledBackgroundColor
                         )
                     },
                     border = { enabled ->
@@ -404,8 +399,8 @@
         modifier: Modifier = Modifier,
         onClick: () -> Unit = {},
         enabled: Boolean = true,
-        background: @Composable (enabled: Boolean) -> State<Painter> = {
-            remember { mutableStateOf(ColorPainter(DEFAULT_SHAPE_COLOR)) }
+        backgroundColor: @Composable (enabled: Boolean) -> State<Color> = {
+            rememberUpdatedState(DEFAULT_SHAPE_COLOR)
         },
         interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
         shape: Shape = CircleShape,
@@ -415,7 +410,7 @@
         onClick = onClick,
         modifier = modifier,
         enabled = enabled,
-        background = background,
+        backgroundColor = backgroundColor,
         interactionSource = interactionSource,
         shape = shape,
         border = border,
diff --git a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Button.kt b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Button.kt
index d7a5406..67e06e3 100644
--- a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Button.kt
+++ b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Button.kt
@@ -17,6 +17,7 @@
 
 import androidx.annotation.RestrictTo
 import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.Interaction
@@ -30,10 +31,8 @@
 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.Color
 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
 
@@ -50,7 +49,7 @@
  * @param modifier Modifier to be applied to the button.
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
  * be clickable.
- * @param background Resolves the background for this button in different states.
+ * @param backgroundColor Resolves the background for this button in different states.
  * @param interactionSource The [MutableInteractionSource] representing the stream of
  * [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
@@ -67,7 +66,7 @@
     onClick: () -> Unit,
     modifier: Modifier,
     enabled: Boolean,
-    background: @Composable (enabled: Boolean) -> State<Painter>,
+    backgroundColor: @Composable (enabled: Boolean) -> State<Color>,
     interactionSource: MutableInteractionSource,
     shape: Shape,
     border: @Composable (enabled: Boolean) -> State<BorderStroke?>?,
@@ -93,13 +92,13 @@
             )
             .size(buttonSize)
             .clip(shape) // Clip for the painted background area after size has been applied.
-            .paint(
-                painter = background(enabled).value,
-                contentScale = ContentScale.Crop
-            )
             .then(
                 if (borderStroke != null) Modifier.border(border = borderStroke, shape = shape)
                 else Modifier
+            )
+            .background(
+                color = backgroundColor(enabled).value,
+                shape = shape
             ),
         content = content
     )
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index fb0f899..02e12cd 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -706,7 +706,7 @@
     method public final androidx.compose.runtime.State<java.lang.Float> getOffset();
     method public final androidx.compose.runtime.State<java.lang.Float> getOverflow();
     method public final androidx.wear.compose.material.SwipeProgress<T> getProgress();
-    method public final T! getTargetValue();
+    method public final T getTargetValue();
     method public final boolean isAnimationRunning();
     method @androidx.wear.compose.material.ExperimentalWearMaterialApi public final suspend Object? performFling(float velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @androidx.wear.compose.material.ExperimentalWearMaterialApi public final suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -716,7 +716,7 @@
     property @androidx.wear.compose.material.ExperimentalWearMaterialApi public final androidx.compose.runtime.State<java.lang.Float> offset;
     property @androidx.wear.compose.material.ExperimentalWearMaterialApi public final androidx.compose.runtime.State<java.lang.Float> overflow;
     property @androidx.wear.compose.material.ExperimentalWearMaterialApi public final androidx.wear.compose.material.SwipeProgress<T> progress;
-    property @androidx.wear.compose.material.ExperimentalWearMaterialApi public final T! targetValue;
+    property @androidx.wear.compose.material.ExperimentalWearMaterialApi public final T targetValue;
     field public static final androidx.wear.compose.material.SwipeableState.Companion Companion;
   }
 
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
index d84fbcb..b298e04 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
@@ -31,7 +31,6 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.painter.ColorPainter
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 
@@ -159,7 +158,7 @@
         onClick = onClick,
         modifier = modifier,
         enabled = enabled,
-        background = { rememberUpdatedState(ColorPainter(colors.backgroundColor(it).value)) },
+        backgroundColor = { colors.backgroundColor(it) },
         interactionSource = interactionSource,
         shape = shape,
         border = { border.borderStroke(enabled = it) },
@@ -341,7 +340,7 @@
             .padding(backgroundPadding)
             .requiredSize(ButtonDefaults.ExtraSmallButtonSize),
         enabled = enabled,
-        background = { rememberUpdatedState(ColorPainter(colors.backgroundColor(it).value)) },
+        backgroundColor = { colors.backgroundColor(it) },
         interactionSource = interactionSource,
         shape = shape,
         border = { border.borderStroke(it) },
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index ccd81a2..cf3b196 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -115,12 +115,52 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
   }
 
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor);
+    method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
+    method public float getExtraSmallButtonSize();
+    method public float getLargeButtonSize();
+    method public float getLargeIconSize();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getShape();
+    method public float getSmallButtonSize();
+    method public float getSmallIconSize();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method public float iconSizeFor(float size);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled, optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor);
+    property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
+    property public final float ExtraSmallButtonSize;
+    property public final float LargeButtonSize;
+    property public final float LargeIconSize;
+    property public final float SmallButtonSize;
+    property public final float SmallIconSize;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape shape;
+    field public static final androidx.wear.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void FilledTonalIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void IconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void OutlinedIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class IconKt {
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class InteractiveComponentSizeKt {
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+  }
+
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
@@ -180,6 +220,10 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  public final class TouchTargetAwareSizeKt {
+    method public static androidx.compose.ui.Modifier touchTargetAwareSize(androidx.compose.ui.Modifier, float size);
+  }
+
   @androidx.compose.runtime.Immutable public final class Typography {
     ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
     method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
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 ccd81a2..a23926c 100644
--- a/wear/compose/compose-material3/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material3/api/public_plus_experimental_current.txt
@@ -115,12 +115,57 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
   }
 
+  @kotlin.RequiresOptIn(message="This Wear Material3 API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearMaterial3Api {
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor);
+    method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
+    method public float getExtraSmallButtonSize();
+    method public float getLargeButtonSize();
+    method public float getLargeIconSize();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getShape();
+    method public float getSmallButtonSize();
+    method public float getSmallIconSize();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method public float iconSizeFor(float size);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled, optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor);
+    property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
+    property public final float ExtraSmallButtonSize;
+    property public final float LargeButtonSize;
+    property public final float LargeIconSize;
+    property public final float SmallButtonSize;
+    property public final float SmallIconSize;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape shape;
+    field public static final androidx.wear.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void FilledTonalIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void IconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void OutlinedIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class IconKt {
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class InteractiveComponentSizeKt {
+    method @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumInteractiveComponentEnforcement();
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+    property @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumInteractiveComponentEnforcement;
+  }
+
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
@@ -180,6 +225,10 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  public final class TouchTargetAwareSizeKt {
+    method public static androidx.compose.ui.Modifier touchTargetAwareSize(androidx.compose.ui.Modifier, float size);
+  }
+
   @androidx.compose.runtime.Immutable public final class Typography {
     ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
     method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index ccd81a2..cf3b196 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -115,12 +115,52 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
   }
 
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor);
+    method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
+    method public float getExtraSmallButtonSize();
+    method public float getLargeButtonSize();
+    method public float getLargeIconSize();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getShape();
+    method public float getSmallButtonSize();
+    method public float getSmallIconSize();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method public float iconSizeFor(float size);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled, optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor);
+    property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
+    property public final float ExtraSmallButtonSize;
+    property public final float LargeButtonSize;
+    property public final float LargeIconSize;
+    property public final float SmallButtonSize;
+    property public final float SmallIconSize;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape shape;
+    field public static final androidx.wear.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void FilledTonalIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void IconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, 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 public static void OutlinedIconButton(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.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class IconKt {
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class InteractiveComponentSizeKt {
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+  }
+
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
@@ -180,6 +220,10 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  public final class TouchTargetAwareSizeKt {
+    method public static androidx.compose.ui.Modifier touchTargetAwareSize(androidx.compose.ui.Modifier, float size);
+  }
+
   @androidx.compose.runtime.Immutable public final class Typography {
     ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
     method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index ede1df5..58f7beb 100644
--- a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -354,7 +354,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledContainerAlpha
+                alpha = DisabledBorderAndContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -380,7 +380,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledTonalButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledContainerAlpha
+                alpha = DisabledBorderAndContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -430,7 +430,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.childButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledContainerAlpha
+                alpha = DisabledBorderAndContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -689,7 +689,7 @@
 }
 
 @RequiresApi(Build.VERSION_CODES.O)
-internal fun ComposeContentTestRule.isShape(
+private fun ComposeContentTestRule.isShape(
     expectedShape: Shape,
     colors: @Composable () -> ButtonColors,
     content: @Composable (Modifier) -> Unit
@@ -726,4 +726,4 @@
         )
 }
 
-const val DisabledContainerAlpha = 0.12f
\ No newline at end of file
+val MinimumButtonTapSize = 48.dp
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
new file mode 100644
index 0000000..d12eb43
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -0,0 +1,497 @@
+/*
+ * 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.padding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertShape
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+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.unit.dp
+import androidx.wear.compose.material3.IconButtonDefaults.DefaultButtonSize
+import androidx.wear.compose.material3.IconButtonDefaults.ExtraSmallButtonSize
+import androidx.wear.compose.material3.IconButtonDefaults.LargeButtonSize
+import androidx.wear.compose.material3.IconButtonDefaults.SmallButtonSize
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+class IconButtonTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun supports_testtag() {
+        rule.setContentWithTheme {
+            IconButton(
+                modifier = Modifier.testTag(TEST_TAG),
+                onClick = {}
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun has_click_action_when_enabled() {
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun has_click_action_when_disabled() {
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun is_correctly_enabled() {
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun is_correctly_disabled() {
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun responds_to_click_when_enabled() {
+        var clicked = false
+
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = { clicked = true },
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performClick()
+
+        rule.runOnIdle {
+            assertEquals(true, clicked)
+        }
+    }
+
+    @Test
+    fun does_not_respond_to_click_when_disabled() {
+        var clicked = false
+
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = { clicked = true },
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performClick()
+
+        rule.runOnIdle {
+            assertEquals(false, clicked)
+        }
+    }
+
+    @Test
+    fun has_role_button() {
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Button
+                )
+            )
+    }
+
+    @Test
+    fun gives_default_button_correct_tap_size() {
+        rule.verifyTapSize(DefaultButtonSize) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier.touchTargetAwareSize(DefaultButtonSize)
+            ) {
+                TestImage()
+            }
+        }
+    }
+
+    @Test
+    fun gives_large_button_correct_tap_size() {
+        rule.verifyTapSize(LargeButtonSize) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier.touchTargetAwareSize(LargeButtonSize)
+            ) {
+                TestImage()
+            }
+        }
+    }
+
+    @Test
+    fun gives_small_button_correct_tap_size() {
+        rule.verifyTapSize(SmallButtonSize) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier.touchTargetAwareSize(SmallButtonSize)
+            ) {
+                TestImage()
+            }
+        }
+    }
+
+    @Test
+    fun gives_extra_small_button_correct_tap_size() {
+        rule.verifyTapSize(
+            expectedSize = MinimumButtonTapSize
+        ) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier.touchTargetAwareSize(ExtraSmallButtonSize)
+            ) {
+                TestImage()
+            }
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun default_shape_is_circular() {
+        rule.isShape(
+            expectedShape = CircleShape,
+            colors = { IconButtonDefaults.iconButtonColors() }
+        ) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier
+            ) {
+                // omit content to allow us to validate the shape by pixel checking.
+            }
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_shape_override() {
+        val shape = CutCornerShape(4.dp)
+
+        rule.isShape(
+            expectedShape = shape,
+            colors = { IconButtonDefaults.iconButtonColors() }
+        ) { modifier ->
+            IconButton(
+                onClick = {},
+                modifier = modifier,
+                shape = shape
+            ) {
+                // omit content to allow us to validate the shape by pixel checking.
+            }
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_enabled_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Enabled,
+            colors = { IconButtonDefaults.iconButtonColors() },
+            expectedContainerColor = { Color.Transparent },
+            expectedContentColor = { MaterialTheme.colorScheme.onBackground }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Disabled,
+            colors = { IconButtonDefaults.iconButtonColors() },
+            expectedContainerColor = { Color.Transparent },
+            expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = ContentAlpha.disabled
+            ) }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_enabled_filled_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Enabled,
+            colors = { IconButtonDefaults.filledIconButtonColors() },
+            expectedContainerColor = { MaterialTheme.colorScheme.primary },
+            expectedContentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_filled_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Disabled,
+            colors = { IconButtonDefaults.filledIconButtonColors() },
+            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = DisabledBorderAndContainerAlpha
+            ) },
+            expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = ContentAlpha.disabled
+            ) }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_enabled_filled_tonal_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Enabled,
+            colors = { IconButtonDefaults.filledTonalIconButtonColors() },
+            expectedContainerColor = { MaterialTheme.colorScheme.surface },
+            expectedContentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_filled_tonal_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Disabled,
+            colors = { IconButtonDefaults.filledTonalIconButtonColors() },
+            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = DisabledBorderAndContainerAlpha
+            ) },
+            expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = ContentAlpha.disabled
+            ) }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_enabled_outlined_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Enabled,
+            colors = { IconButtonDefaults.outlinedIconButtonColors() },
+            expectedContainerColor = { Color.Transparent },
+            expectedContentColor = { MaterialTheme.colorScheme.primary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_outlined_icon_button_colors() {
+        rule.verifyIconButtonColors(
+            status = Status.Disabled,
+            colors = { IconButtonDefaults.outlinedIconButtonColors() },
+            expectedContainerColor = { Color.Transparent },
+            expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
+                alpha = ContentAlpha.disabled
+            ) }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_enabled_outlined_icon_button_correct_border_colors() {
+        val status = Status.Enabled
+        rule.verifyButtonBorderColor(
+            expectedBorderColor = { MaterialTheme.colorScheme.outline },
+            content = { modifier: Modifier ->
+                OutlinedIconButton(
+                    onClick = {},
+                    modifier = modifier,
+                    enabled = status.enabled()
+                ) {}
+            }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_outlined_icon_button_correct_border_colors() {
+        val status = Status.Disabled
+        rule.verifyButtonBorderColor(
+            expectedBorderColor = {
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+            },
+            content = { modifier: Modifier ->
+                OutlinedIconButton(
+                    onClick = {},
+                    modifier = modifier,
+                    enabled = status.enabled()
+                ) {}
+            }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun overrides_outlined_icon_button_border_color() {
+        val status = Status.Enabled
+        rule.verifyButtonBorderColor(
+            expectedBorderColor = { Color.Green },
+            content = { modifier: Modifier ->
+                OutlinedIconButton(
+                    onClick = {},
+                    modifier = modifier,
+                    enabled = status.enabled(),
+                    border = ButtonDefaults.outlinedButtonBorder(
+                        enabled = status.enabled(),
+                        borderColor = Color.Green,
+                        disabledBorderColor = Color.Red
+                    )
+                ) {}
+            }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.verifyIconButtonColors(
+        status: Status,
+        colors: @Composable () -> IconButtonColors,
+        expectedContainerColor: @Composable () -> Color,
+        expectedContentColor: @Composable () -> Color,
+    ) {
+        verifyColors(
+            status = status,
+            expectedContainerColor = expectedContainerColor,
+            expectedContentColor = expectedContentColor,
+            applyAlphaForDisabled = false,
+            content = {
+                var actualContentColor = Color.Transparent
+                IconButton(
+                    onClick = {},
+                    enabled = status.enabled(),
+                    colors = colors(),
+                    modifier = Modifier.testTag(TEST_TAG),
+                ) {
+                    actualContentColor = LocalContentColor.current
+                }
+                return@verifyColors actualContentColor
+            }
+        )
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+private fun ComposeContentTestRule.isShape(
+    expectedShape: Shape,
+    colors: @Composable () -> IconButtonColors,
+    content: @Composable (Modifier) -> Unit
+) {
+    var background = Color.Transparent
+    var buttonColor = Color.Transparent
+    val padding = 0.dp
+
+    setContentWithTheme {
+        background = MaterialTheme.colorScheme.surface
+        Box(Modifier.background(background)) {
+            buttonColor = colors().containerColor(true).value
+            if (buttonColor == Color.Transparent) {
+                buttonColor = background
+            }
+            content(
+                Modifier
+                    .testTag(TEST_TAG)
+                    .padding(padding)
+            )
+        }
+    }
+
+    onNodeWithTag(TEST_TAG)
+        .captureToImage()
+        .assertShape(
+            density = density,
+            horizontalPadding = 0.dp,
+            verticalPadding = 0.dp,
+            shapeColor = buttonColor,
+            backgroundColor = background,
+            shapeOverlapPixelCount = 2.0f,
+            shape = expectedShape,
+        )
+}
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 6642e9a..750e25e 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
@@ -38,6 +38,8 @@
 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.test.captureToImage
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -121,6 +123,20 @@
     }
 }
 
+internal fun ComposeContentTestRule.verifyTapSize(
+    expectedSize: Dp,
+    content: @Composable (modifier: Modifier) -> Unit
+) {
+    setContentWithTheme {
+        content(Modifier.testTag(TEST_TAG))
+    }
+    waitForIdle()
+
+    onNodeWithTag(TEST_TAG)
+        .assertTouchHeightIsEqualTo(expectedSize)
+        .assertTouchWidthIsEqualTo(expectedSize)
+}
+
 @RequiresApi(Build.VERSION_CODES.O)
 internal fun ComposeContentTestRule.verifyColors(
     status: Status,
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 8eaa073..fa6786f 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
@@ -381,7 +381,7 @@
             secondaryLabel
         ) },
         icon = icon?.let {
-            provideIcon(colors.iconColor(enabled), icon)
+            provideScopeContent(colors.iconColor(enabled), icon)
         },
         defaultIconSpacing = ButtonDefaults.IconSpacing,
         role = role
@@ -917,8 +917,6 @@
      * [Button].
      */
     internal val IconSpacing = 6.dp
-
-    internal const val DisabledBorderAndContainerAlpha = 0.12f
 }
 
 /**
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt
index 91e559b..8471524 100644
--- a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt
@@ -122,3 +122,5 @@
     const val medium: Float = 0.60f
     const val disabled: Float = 0.38f
 }
+
+internal const val DisabledBorderAndContainerAlpha = 0.12f
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ExperimentalWearMaterial3Api.kt
similarity index 69%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt
rename to wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ExperimentalWearMaterial3Api.kt
index 3407f2c..1d369e6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ExperimentalWearMaterial3Api.kt
@@ -14,17 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.compose.foundation.lazy.layout
+package androidx.wear.compose.material3
 
-internal interface BeyondBoundsState {
-
-    fun remeasure()
-
-    val itemCount: Int
-
-    val hasVisibleItems: Boolean
-
-    val firstVisibleIndex: Int
-
-    val lastVisibleIndex: Int
-}
\ No newline at end of file
+@RequiresOptIn(
+    "This Wear Material3 API is experimental and is likely to change or to be removed in" +
+        " the future."
+)
+@Retention(AnnotationRetention.BINARY)
+annotation class ExperimentalWearMaterial3Api
\ 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
new file mode 100644
index 0000000..087dd93
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/IconButton.kt
@@ -0,0 +1,472 @@
+/*
+ * 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.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.max
+
+/**
+ * Wear Material [IconButton] is a circular, icon-only button with transparent background and
+ * no border. It offers a single slot to take icon or image content.
+ *
+ * Set the size of the [IconButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the recommended minimum touch target size is available.
+ *
+ * The recommended [IconButton] sizes are [IconButtonDefaults.DefaultButtonSize],
+ * [IconButtonDefaults.LargeButtonSize], [IconButtonDefaults.SmallButtonSize] and
+ * [IconButtonDefaults.ExtraSmallButtonSize].
+ *
+ * Use [IconButtonDefaults.iconSizeFor] to determine the icon size for a
+ * given [IconButtonDefaults] size, or refer to icon sizes [IconButtonDefaults.SmallIconSize],
+ * [IconButtonDefaults.DefaultIconSize], [IconButtonDefaults.LargeButtonSize] directly.
+ *
+ * [IconButton] can be enabled or disabled. A disabled button will not respond to click events.
+ *
+ * TODO(b/261838497) Add Material3 samples and UX guidance links
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param shape Defines the icon button's shape. It is strongly recommended to use the default
+ * as this shape is a key characteristic of the Wear Material3 design.
+ * @param colors [IconButtonColors] that will be used to resolve the background and icon color for
+ * this button in different states.
+ * @param border Optional [BorderStroke] for the icon button border.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [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 content The content displayed on the icon button, expected to be icon or image.
+ */
+@Composable
+fun IconButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = IconButtonDefaults.shape,
+    colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
+    border: BorderStroke? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable BoxScope.() -> Unit,
+) {
+    androidx.wear.compose.materialcore.Button(
+        onClick = onClick,
+        modifier.minimumInteractiveComponentSize(),
+        enabled = enabled,
+        backgroundColor = { colors.containerColor(enabled = it) },
+        interactionSource = interactionSource,
+        shape = shape,
+        border = { rememberUpdatedState(border) },
+        buttonSize = IconButtonDefaults.DefaultButtonSize,
+        content = provideScopeContent(
+            colors.contentColor(enabled = enabled),
+            content
+        )
+    )
+}
+
+/**
+ * Wear Material [FilledIconButton] is a circular, icon-only button with a colored background
+ * and a contrasting content color. It offers a single slot to take an icon or image.
+ *
+ * Set the size of the [FilledIconButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the recommended minimum touch target size is available.
+ *
+ * The recommended [IconButton] sizes are [IconButtonDefaults.DefaultButtonSize],
+ * [IconButtonDefaults.LargeButtonSize], [IconButtonDefaults.SmallButtonSize] and
+ * [IconButtonDefaults.ExtraSmallButtonSize].
+ *
+ * Use [IconButtonDefaults.iconSizeFor] to determine the icon size for a
+ * given [IconButton] size, or refer to icon sizes [IconButtonDefaults.SmallIconSize],
+ * [IconButtonDefaults.DefaultIconSize], [IconButtonDefaults.LargeIconSize] directly.
+ *
+ * [FilledIconButton] can be enabled or disabled. A disabled button will not respond to
+ * click events.
+ *
+ * TODO(b/261838497) Add Material3 samples and UX guidance links
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param shape Defines the icon button's shape. It is strongly recommended to use the default
+ * as this shape is a key characteristic of the Wear Material3 design.
+ * @param colors [IconButtonColors] that will be used to resolve the container and content color for
+ * this icon button in different states.
+ * @param border Optional [BorderStroke] for the icon button border.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [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 content The content displayed on the icon button, expected to be icon or image.
+ */
+@Composable
+fun FilledIconButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = IconButtonDefaults.shape,
+    colors: IconButtonColors = IconButtonDefaults.filledIconButtonColors(),
+    border: BorderStroke? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable BoxScope.() -> Unit,
+) = IconButton(onClick, modifier, enabled, shape, colors, border, interactionSource, content)
+
+/**
+ * Wear Material [FilledTonalIconButton] is a circular, icon-only button with a muted, colored
+ * background and a contrasting icon color. It offers a single slot to take an icon or image.
+ *
+ * Set the size of the [FilledTonalIconButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the recommended minimum touch target size is available.
+ *
+ * The recommended icon button sizes are [IconButtonDefaults.DefaultButtonSize],
+ * [IconButtonDefaults.LargeButtonSize], [IconButtonDefaults.SmallButtonSize] and
+ * [IconButtonDefaults.ExtraSmallButtonSize].
+ *
+ * Use [IconButtonDefaults.iconSizeFor] to determine the icon size for a
+ * given [IconButtonDefaults] size, or refer to icon sizes [IconButtonDefaults.SmallIconSize],
+ * [IconButtonDefaults.DefaultIconSize], [IconButtonDefaults.LargeButtonSize] directly.
+ *
+ * [FilledTonalIconButton] can be enabled or disabled.
+ * A disabled button will not respond to click events.
+ *
+ * TODO(b/261838497) Add Material3 samples and UX guidance links
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param shape Defines the icon button's shape. It is strongly recommended to use the default
+ * as this shape is a key characteristic of the Wear Material3 design.
+ * @param colors [IconButtonColors] that will be used to resolve the background and icon color for
+ * this button in different states.
+ * @param border Optional [BorderStroke] for the icon button border.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [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 content The content displayed on the icon button, expected to be icon or image.
+ */
+@Composable
+fun FilledTonalIconButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = IconButtonDefaults.shape,
+    colors: IconButtonColors = IconButtonDefaults.filledTonalIconButtonColors(),
+    border: BorderStroke? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable BoxScope.() -> Unit,
+) = IconButton(onClick, modifier, enabled, shape, colors, border, interactionSource, content)
+
+/**
+ * Wear Material [OutlinedIconButton] is a circular, icon-only button with a transparent background,
+ * contrasting icon color and border. It offers a single slot to take an icon or image.
+ *
+ * Set the size of the [OutlinedIconButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the recommended minimum touch target size is available.
+ *
+ * The recommended icon button sizes are [IconButtonDefaults.DefaultButtonSize],
+ * [IconButtonDefaults.LargeButtonSize], [IconButtonDefaults.SmallButtonSize] and
+ * [IconButtonDefaults.ExtraSmallButtonSize].
+ *
+ * Use [IconButtonDefaults.iconSizeFor] to determine the icon size for a
+ * given [IconButtonDefaults] size, or refer to icon sizes [IconButtonDefaults.SmallIconSize],
+ * [IconButtonDefaults.DefaultIconSize], [IconButtonDefaults.LargeButtonSize] directly.
+ *
+ * [OutlinedIconButton] can be enabled or disabled.
+ * A disabled button will not respond to click events.
+ *
+ * An [OutlinedIconButton] has a transparent background and a thin border by default with
+ * content taking the theme primary color.
+ *
+ * TODO(b/261838497) Add Material3 samples and UX guidance links
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param shape Defines the icon button's shape. It is strongly recommended to use the default
+ * as this shape is a key characteristic of the Wear Material3 design.
+ * @param colors [IconButtonColors] that will be used to resolve the background and icon color for
+ * this button in different states. See [IconButtonDefaults.outlinedIconButtonBorder].
+ * @param border Optional [BorderStroke] for the icon button border.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [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 content The content displayed on the icon button, expected to be icon or image.
+ */
+@Composable
+fun OutlinedIconButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = IconButtonDefaults.shape,
+    colors: IconButtonColors = IconButtonDefaults.outlinedIconButtonColors(),
+    border: BorderStroke? = IconButtonDefaults.outlinedIconButtonBorder(enabled),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable BoxScope.() -> Unit,
+) = IconButton(onClick, modifier, enabled, shape, colors, border, interactionSource, content)
+
+/**
+ * Contains the default values used by [IconButton].
+ */
+object IconButtonDefaults {
+    /**
+     * Recommended [Shape] for [IconButton].
+     */
+    val shape = CircleShape
+
+    /**
+     * Recommended icon size for a given icon button size.
+     *
+     * Ensures that the minimum recommended icon size is applied.
+     *
+     * Examples: for size [LargeButtonSize], returns [LargeIconSize],
+     * for size [ExtraSmallButtonSize] returns [SmallIconSize].
+     *
+     * @param size The size of the icon button
+     */
+    fun iconSizeFor(size: Dp): Dp = max(SmallIconSize, size / 2f)
+
+    /**
+     * Creates a [ButtonColors] with the colors for [FilledIconButton] - by default,
+     * a colored background with a contrasting icon color.
+     * If the icon button is disabled then the colors will default to
+     * the MaterialTheme onSurface color with suitable alpha values applied.
+     *
+     * @param containerColor The background color of this icon button when enabled
+     * @param contentColor The color of this icon button when enabled
+     */
+    @Composable
+    fun filledIconButtonColors(
+        containerColor: Color = MaterialTheme.colorScheme.primary,
+        contentColor: Color = MaterialTheme.colorScheme.onPrimary,
+    ): IconButtonColors {
+        return iconButtonColors(
+            containerColor = containerColor,
+            contentColor = contentColor
+        )
+    }
+
+    /**
+     * Creates a [ButtonColors] with the colors for [FilledTonalIconButton]- by default,
+     * a muted colored background with a contrasting icon color.
+     * If the icon button is disabled then the colors will default to
+     * the MaterialTheme onSurface color with suitable alpha values applied.
+     *
+     * @param containerColor The background color of this icon button when enabled
+     * @param contentColor The color of this icon button when enabled
+     */
+    @Composable
+    fun filledTonalIconButtonColors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+    ): IconButtonColors {
+        return iconButtonColors(
+            containerColor = containerColor,
+            contentColor = contentColor
+        )
+    }
+
+    /**
+     * Creates a [ButtonColors] with the colors for [OutlinedIconButton]- by default,
+     * a transparent background with contrasting icon color.
+     * If the icon button is disabled then the colors will default to
+     * the MaterialTheme onSurface color with suitable alpha values applied.
+     *
+     * @param contentColor The color of this icon button when enabled
+     */
+    @Composable
+    fun outlinedIconButtonColors(
+        contentColor: Color = MaterialTheme.colorScheme.primary,
+    ): IconButtonColors {
+        return iconButtonColors(
+            containerColor = Color.Transparent,
+            contentColor = contentColor
+        )
+    }
+
+    /**
+     * Creates a [ButtonColors] with the colors for [IconButton] - by default,
+     * a transparent background with a contrasting icon color.
+     * If the icon button is disabled then the colors will default to
+     * the MaterialTheme onSurface color with suitable alpha values applied.
+     *
+     * @param containerColor the background color of this icon button when enabled
+     * @param contentColor the color of this icon when enabled
+     * @param disabledContainerColor the background color of this icon button when not enabled
+     * @param disabledContentColor the color of this icon when not enabled
+     */
+    @Composable
+    fun iconButtonColors(
+        containerColor: Color = Color.Transparent,
+        contentColor: Color = MaterialTheme.colorScheme.onBackground,
+        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.copy(
+            alpha = DisabledBorderAndContainerAlpha
+        ),
+        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(
+            alpha = ContentAlpha.disabled
+        )
+    ): IconButtonColors = IconButtonColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor,
+    )
+
+    /**
+     * Creates a [BorderStroke], such as for an [OutlinedIconButton]
+     *
+     * @param borderColor The color to use for the border for this outline when enabled
+     * @param disabledBorderColor The color to use for the border for this outline when
+     * disabled
+     * @param borderWidth The width to use for the border for this outline. It is strongly
+     * recommended to use the default width as this outline is a key characteristic
+     * of Wear Material3.
+     */
+    @Composable
+    fun outlinedIconButtonBorder(
+        enabled: Boolean,
+        borderColor: Color = MaterialTheme.colorScheme.outline,
+        disabledBorderColor: Color = MaterialTheme.colorScheme.onSurface.copy(
+            alpha = DisabledBorderAndContainerAlpha
+        ),
+        borderWidth: Dp = 1.dp
+    ): BorderStroke {
+        return remember {
+            BorderStroke(borderWidth, if (enabled) borderColor else disabledBorderColor)
+        }
+    }
+
+    /**
+     * The recommended size of an icon when used inside an icon button with size
+     * [SmallButtonSize] or [ExtraSmallButtonSize].
+     * Use [iconSizeFor] to easily determine the icon size.
+     */
+    val SmallIconSize = 24.dp
+
+    /**
+     * The default size of an icon when used inside an icon button of size DefaultButtonSize.
+     * Use [iconSizeFor] to easily determine the icon size.
+     */
+    val DefaultIconSize = 26.dp
+
+    /**
+     * The size of an icon when used inside an icon button with size [LargeButtonSize].
+     * Use [iconSizeFor] to easily determine the icon size.
+     */
+    val LargeIconSize = 30.dp
+
+    /**
+     * The recommended background size of an extra small, compact button.
+     * It is recommended to apply this size using Modifier.touchTargetAwareSize.
+     */
+    val ExtraSmallButtonSize = 32.dp
+
+    /**
+     * The recommended size for a small button.
+     * It is recommended to apply this size using Modifier.touchTargetAwareSize.
+     */
+    val SmallButtonSize = 48.dp
+
+    /**
+     * The default size applied for buttons.
+     * It is recommended to apply this size using Modifier.touchTargetAwareSize.
+     */
+    val DefaultButtonSize = 52.dp
+
+    /**
+     * The recommended size for a large button.
+     * It is recommended to apply this size using Modifier.touchTargetAwareSize.
+     */
+    val LargeButtonSize = 60.dp
+}
+
+/**
+ * Represents the container and content colors used in an icon button in different states.
+ *
+ * - See [IconButtonDefaults.filledIconButtonColors] and
+ * [IconButtonDefaults.filledTonalIconButtonColors] for the default colors used in a
+ * [FilledIconButton].
+ * - See [IconButtonDefaults.outlinedIconButtonColors] for the default colors used in an
+ * [OutlinedIconButton].
+ */
+@Immutable
+class IconButtonColors internal constructor(
+    private val containerColor: Color,
+    private val contentColor: Color,
+    private val disabledContainerColor: Color,
+    private val disabledContentColor: Color,
+) {
+    /**
+     * Represents the container color for this icon button, depending on [enabled].
+     *
+     * @param enabled whether the icon button is enabled
+     */
+    @Composable
+    internal fun containerColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
+    }
+
+    /**
+     * Represents the content color for this icon button, depending on [enabled].
+     *
+     * @param enabled whether the icon button is enabled
+     */
+    @Composable
+    internal fun contentColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is IconButtonColors) return false
+
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledContentColor != other.disabledContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledContentColor.hashCode()
+
+        return result
+    }
+}
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/InteractiveComponentSize.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/InteractiveComponentSize.kt
new file mode 100644
index 0000000..dc96844
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/InteractiveComponentSize.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
+
+/**
+ * Reserves at least 48.dp in size to disambiguate touch interactions if the element would measure
+ * smaller.
+ *
+ * https://m3.material.io/foundations/accessible-design/accessibility-basics#28032e45-c598-450c-b355-f9fe737b1cd8
+ *
+ * This uses the Material recommended minimum size of 48.dp x 48.dp, which may not the same as the
+ * system enforced minimum size.
+ *
+ * This modifier is not needed for touch target expansion to happen. It only affects layout, to make
+ * sure there is adequate space for touch target expansion.
+ */
+@OptIn(ExperimentalWearMaterial3Api::class)
+fun Modifier.minimumInteractiveComponentSize(): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "minimumInteractiveComponentSize"
+        // TODO: b/214589635 - surface this information through the layout inspector in a better way
+        //  - for now just add some information to help developers debug what this size represents.
+        properties["README"] = "Reserves at least 48.dp in size to disambiguate touch " +
+            "interactions if the element would measure smaller"
+    }
+) {
+    if (LocalMinimumInteractiveComponentEnforcement.current) {
+        MinimumInteractiveComponentSizeModifier(minimumInteractiveComponentSize)
+    } else {
+        Modifier
+    }
+}
+
+/**
+ * CompositionLocal that configures whether Wear Material components that have a visual size that is
+ * lower than the minimum touch target size for accessibility (such as Button) will include
+ * extra space outside the component to ensure that they are accessible. If set to false there
+ * will be no extra space, and so it is possible that if the component is placed near the edge of
+ * a layout / near to another component without any padding, there will not be enough space for
+ * an accessible touch target.
+ */
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@get:ExperimentalWearMaterial3Api
+@ExperimentalWearMaterial3Api
+val LocalMinimumInteractiveComponentEnforcement: ProvidableCompositionLocal<Boolean> =
+    staticCompositionLocalOf { true }
+
+private class MinimumInteractiveComponentSizeModifier(val size: DpSize) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+
+        val placeable = measurable.measure(constraints)
+
+        // Be at least as big as the minimum dimension in both dimensions
+        val width = maxOf(placeable.width, size.width.roundToPx())
+        val height = maxOf(placeable.height, size.height.roundToPx())
+
+        return layout(width, height) {
+            val centerX = ((width - placeable.width) / 2f).roundToInt()
+            val centerY = ((height - placeable.height) / 2f).roundToInt()
+            placeable.place(centerX, centerY)
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        val otherModifier = other as? MinimumInteractiveComponentSizeModifier ?: return false
+        return size == otherModifier.size
+    }
+
+    override fun hashCode(): Int {
+        return size.hashCode()
+    }
+}
+
+private val minimumInteractiveComponentSize: DpSize = DpSize(48.dp, 48.dp)
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Providers.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Providers.kt
index c1f9506..a8ad2b6 100644
--- a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Providers.kt
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Providers.kt
@@ -16,7 +16,6 @@
 
 package androidx.wear.compose.material3
 
-import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.State
@@ -38,14 +37,13 @@
     }
 }
 
-internal fun provideIcon(
-    iconColor: State<Color>,
-    content: (@Composable BoxScope.() -> Unit)
-): (@Composable BoxScope.() -> Unit) = {
-    val color = iconColor.value
+internal fun <T> provideScopeContent(
+    color: State<Color>,
+    content: (@Composable T.() -> Unit)
+): (@Composable T.() -> Unit) = {
     CompositionLocalProvider(
-        LocalContentColor provides color,
-        LocalContentAlpha provides color.alpha,
+        LocalContentColor provides color.value,
+        LocalContentAlpha provides color.value.alpha,
     ) {
         content()
     }
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/TouchTargetAwareSize.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/TouchTargetAwareSize.kt
new file mode 100644
index 0000000..45dda91
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/TouchTargetAwareSize.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.max
+
+/**
+ * Modifier to set both the size and recommended touch target for
+ * [IconButton] and TextButton.
+ */
+fun Modifier.touchTargetAwareSize(size: Dp): Modifier =
+    this
+        .padding(PaddingValues(max(0.dp, (minimumTouchTarget - size) * 0.5f)))
+        .size(size)
+
+private val minimumTouchTarget = 48.dp
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index 7bfe4ee..fc1e4dc 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -27,14 +27,14 @@
 
     api(project(":compose:ui:ui"))
     api(project(":compose:runtime:runtime"))
-    api("androidx.navigation:navigation-runtime:2.5.3")
+    api("androidx.navigation:navigation-runtime:2.6.0-beta01")
     api(project(":wear:compose:compose-material"))
     api("androidx.activity:activity-compose:1.7.0")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0")
 
     implementation(libs.kotlinStdlib)
-    implementation(project(":navigation:navigation-common"))
-    implementation("androidx.navigation:navigation-compose:2.5.3")
+    implementation("androidx.navigation:navigation-common:2.6.0-beta01")
+    implementation("androidx.navigation:navigation-compose:2.6.0-beta01")
     implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(project(":compose:test-utils"))
@@ -45,7 +45,7 @@
     androidTestImplementation(project(":wear:compose:compose-navigation-samples"))
     androidTestImplementation(libs.truth)
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.0")
-    androidTestImplementation("androidx.navigation:navigation-testing:2.5.3")
+    androidTestImplementation("androidx.navigation:navigation-testing:2.6.0-beta01")
 
     samples(project(":wear:compose:compose-navigation-samples"))
 }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
index 57fbba0..00e8d98 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
@@ -33,6 +33,7 @@
 import androidx.wear.compose.foundation.samples.SimpleScalingLazyColumn
 import androidx.wear.compose.foundation.samples.SimpleScalingLazyColumnWithContentPadding
 import androidx.wear.compose.foundation.samples.SimpleScalingLazyColumnWithSnap
+import androidx.wear.compose.foundation.samples.SwipeToRevealWithExpandables
 
 val WearFoundationDemos = DemoCategory(
     "Foundation",
@@ -104,14 +105,17 @@
                 ComposableDemo("Swipe To Reveal Chip") {
                     SwipeToRevealChips()
                 },
-                ComposableDemo("Swipe to Reveal Card") {
+                ComposableDemo("Swipe To Reveal Card") {
                     SwipeToRevealCards()
                 },
-                ComposableDemo("Swipe to Reveal - Custom") {
+                ComposableDemo("Swipe To Reveal - Custom") {
                     SwipeToRevealWithSingleAction()
                 },
-                ComposableDemo("Swipe to Reveal - RTL") {
+                ComposableDemo("Swipe To Reveal - RTL") {
                     SwipeToRevealInRtl()
+                },
+                ComposableDemo("Swipe To Reveal - Expandable") {
+                    SwipeToRevealWithExpandables()
                 }
             )
         )
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
index d631e81..e0f9d24 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
@@ -18,7 +18,9 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -37,12 +39,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.foundation.ExpandableState
 import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.RevealScope
 import androidx.wear.compose.foundation.expandableItem
 import androidx.wear.compose.foundation.fractionalPositionalThreshold
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
@@ -58,6 +62,7 @@
 import androidx.wear.compose.material.Text
 import androidx.wear.compose.foundation.createAnchors
 import androidx.wear.compose.foundation.rememberRevealState
+import kotlin.math.abs
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
@@ -225,29 +230,35 @@
     val coroutineScope = rememberCoroutineScope()
     SwipeToReveal(
         action = {
-            DeleteButton(
-                state = state,
+            SwipeToRevealAction(
+                color = MaterialTheme.colors.error,
+                icon = Icons.Outlined.Delete,
+                text = "Clear",
+                contentDescription = "Delete",
+                onClick = { state.animateTo(RevealValue.Revealed) },
+                shape = shape,
                 coroutineScope = coroutineScope,
-                shape = shape
+                state = state
             )
         },
         additionalAction = {
-            MoreOptionsButton(
-                state = state,
+            SwipeToRevealAction(
+                color = MaterialTheme.colors.onSurfaceVariant,
+                icon = Icons.Outlined.MoreVert,
+                onClick = { state.animateTo(RevealValue.Covered) },
+                shape = shape,
                 coroutineScope = coroutineScope,
-                shape = shape
+                state = state
             )
         },
         undoAction = {
-            Box(
-                modifier = Modifier.clickable {
-                    coroutineScope.launch {
-                        state.snapTo(RevealValue.Covered)
-                    }
-                }
-            ) {
-                Text("Undo")
-            }
+            Chip(
+                modifier = Modifier.fillMaxWidth(),
+                onClick = { coroutineScope.launch { state.animateTo(RevealValue.Covered) } },
+                colors = ChipDefaults.secondaryChipColors(),
+                border = ChipDefaults.outlinedChipBorder(),
+                label = { Text(text = "Undo") }
+            )
         },
         state = state,
         content = content,
@@ -259,36 +270,54 @@
 private fun SwipeToRevealSingleAction(
     layoutDirection: LayoutDirection = LayoutDirection.Ltr
 ) {
+    val itemCount = 2
+    val expandableState = List(itemCount) {
+        rememberExpandableState(initiallyExpanded = true)
+    }
     ScalingLazyColumn {
         item {
             Text("Swipe to reveal One-Action")
             Spacer(Modifier.size(10.dp))
         }
-        repeat(2) {
-            item {
+        repeat(itemCount) { curr ->
+            expandableItem(
+                state = expandableState[curr]
+            ) { expanded ->
                 val state = rememberRevealState(
                     anchors = createAnchors(revealingAnchor = 0.5f),
                     positionalThreshold = fractionalPositionalThreshold(0.5f)
                 )
-                CompositionLocalProvider(
-                    LocalLayoutDirection provides layoutDirection
-                ) {
-                    SwipeToReveal(
-                        action = {
-                            DeleteButton(
-                                state = state,
-                                coroutineScope = rememberCoroutineScope(),
-                                shape = CircleShape
-                            )
-                        },
-                        state = state
+                if (expanded) {
+                    CompositionLocalProvider(
+                        LocalLayoutDirection provides layoutDirection
                     ) {
-                        Chip(
-                            onClick = { /*TODO*/ },
-                            colors = ChipDefaults.secondaryChipColors(),
-                            modifier = Modifier.fillMaxWidth(),
-                            label = { Text("Try this") }
-                        )
+                        SwipeToReveal(
+                            action = {
+                                SwipeToRevealAction(
+                                    color = MaterialTheme.colors.error,
+                                    icon = Icons.Outlined.Delete,
+                                    text = "Clear",
+                                    contentDescription = "Delete",
+                                    onClick = { state.animateTo(RevealValue.Revealed) },
+                                    shape = CircleShape,
+                                    state = state
+                                )
+                            },
+                            state = state
+                        ) {
+                            Chip(
+                                onClick = { /*TODO*/ },
+                                colors = ChipDefaults.secondaryChipColors(),
+                                modifier = Modifier.fillMaxWidth(),
+                                label = { Text("Try this") }
+                            )
+                        }
+                    }
+                }
+                LaunchedEffect(state.currentValue) {
+                    if (state.currentValue == RevealValue.Revealed) {
+                        delay(2000)
+                        expandableState[curr].expanded = false
                     }
                 }
             }
@@ -298,48 +327,30 @@
 
 @OptIn(ExperimentalWearFoundationApi::class)
 @Composable
-private fun DeleteButton(
-    state: RevealState,
-    coroutineScope: CoroutineScope,
-    shape: Shape = CircleShape,
+private fun RevealScope.SwipeToRevealAction(
+    color: Color,
+    icon: ImageVector,
+    text: String? = null,
+    contentDescription: String? = null,
+    onClick: suspend () -> Unit = {},
+    shape: Shape = RoundedCornerShape(15.dp),
+    state: RevealState = rememberRevealState(),
+    coroutineScope: CoroutineScope = rememberCoroutineScope(),
 ) {
-    Box(
+    Row(
         modifier = Modifier
             .clickable {
-                coroutineScope.launch { state.animateTo(RevealValue.Revealed) }
+                coroutineScope.launch { onClick() }
             }
-            .background(MaterialTheme.colors.error, shape)
+            .background(color, shape)
             .fillMaxSize(),
-        contentAlignment = Alignment.Center
+        horizontalArrangement = Arrangement.Center,
+        verticalAlignment = Alignment.CenterVertically
     ) {
-        Icon(
-            imageVector = Icons.Outlined.Delete,
-            contentDescription = "Delete",
-            tint = Color.Black,
-        )
-    }
-}
-
-@OptIn(ExperimentalWearFoundationApi::class)
-@Composable
-private fun MoreOptionsButton(
-    state: RevealState,
-    coroutineScope: CoroutineScope,
-    shape: Shape = CircleShape,
-) {
-    Box(
-        modifier = Modifier
-            .clickable {
-                coroutineScope.launch { state.animateTo(RevealValue.Covered) }
-            }
-            .background(MaterialTheme.colors.onSurfaceVariant, shape)
-            .fillMaxSize(),
-        contentAlignment = Alignment.Center
-    ) {
-        Icon(
-            imageVector = Icons.Outlined.MoreVert,
-            contentDescription = "More Options",
-            tint = Color.DarkGray,
-        )
+        Icon(imageVector = icon, contentDescription = contentDescription, tint = Color.DarkGray)
+        if (abs(state.offset) > revealOffset && text != null) {
+            Spacer(Modifier.size(5.dp))
+            Text(text = text)
+        }
     }
 }
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/current.txt b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
index a4ee560..712985a 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
@@ -16,12 +16,9 @@
     method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
   }
 
-  public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+  public class DynamicTypeEvaluator {
     ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
-    method @UiThread public void disablePlatformDataSources();
-    method @UiThread public void enablePlatformDataSources();
   }
 
   public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
     method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
     method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
     method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
-    method public boolean isPlatformDataSourcesInitiallyEnabled();
   }
 
   public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
-    method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.TimeGateway);
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
index a4ee560..712985a 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
@@ -16,12 +16,9 @@
     method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
   }
 
-  public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+  public class DynamicTypeEvaluator {
     ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
-    method @UiThread public void disablePlatformDataSources();
-    method @UiThread public void enablePlatformDataSources();
   }
 
   public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
     method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
     method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
     method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
-    method public boolean isPlatformDataSourcesInitiallyEnabled();
   }
 
   public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
-    method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.TimeGateway);
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
index 72f6bf6..f5ae829 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
@@ -16,12 +16,9 @@
     method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
   }
 
-  public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+  public class DynamicTypeEvaluator {
     ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
-    method @UiThread public void disablePlatformDataSources();
-    method @UiThread public void enablePlatformDataSources();
   }
 
   public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
     method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
     method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
     method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
-    method public boolean isPlatformDataSourcesInitiallyEnabled();
   }
 
   public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
-    method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
     method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.TimeGateway);
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
index 3ece394..068ce4b 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
@@ -21,13 +21,11 @@
 import android.icu.util.ULocale;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
-import androidx.annotation.UiThread;
 import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonFloatNode;
 import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonInt32Node;
@@ -102,7 +100,7 @@
  * <p>It's the callers responsibility to destroy those dynamic types after use, with {@link
  * BoundDynamicType#close()}.
  */
-public class DynamicTypeEvaluator implements AutoCloseable {
+public class DynamicTypeEvaluator {
     private static final String TAG = "DynamicTypeEvaluator";
     private static final QuotaManager NO_OP_QUOTA_MANAGER =
             new FixedQuotaManagerImpl(Integer.MAX_VALUE);
@@ -131,17 +129,14 @@
 
     @NonNull private static final StateStore EMPTY_STATE_STORE = new StateStore(emptyMap());
 
-    @NonNull private final Config mConfig;
     @NonNull private final StateStore mStateStore;
     @NonNull private final QuotaManager mAnimationQuotaManager;
     @NonNull private final QuotaManager mDynamicTypesQuotaManager;
-    @NonNull private final TimeGateway mTimeGateway;
-    @Nullable private final EpochTimePlatformDataSource mTimeDataSource;
+    @NonNull private final EpochTimePlatformDataSource mTimeDataSource;
     @Nullable private final SensorGatewayPlatformDataSource mSensorGatewayDataSource;
 
     /** Configuration for creating {@link DynamicTypeEvaluator}. */
     public static final class Config {
-        private final boolean mPlatformDataSourcesInitiallyEnabled;
         @Nullable private final StateStore mStateStore;
         @Nullable private final QuotaManager mAnimationQuotaManager;
         @Nullable private final TimeGateway mTimeGateway;
@@ -149,13 +144,11 @@
         @Nullable private final QuotaManager mDynamicTypesQuotaManager;
 
         Config(
-                boolean platformDataSourcesInitiallyEnabled,
                 @Nullable StateStore stateStore,
                 @Nullable QuotaManager animationQuotaManager,
                 @Nullable QuotaManager dynamicTypesQuotaManager,
                 @Nullable TimeGateway timeGateway,
                 @Nullable SensorGateway sensorGateway) {
-            this.mPlatformDataSourcesInitiallyEnabled = platformDataSourcesInitiallyEnabled;
             this.mStateStore = stateStore;
             this.mAnimationQuotaManager = animationQuotaManager;
             this.mTimeGateway = timeGateway;
@@ -165,7 +158,6 @@
 
         /** Builds a {@link DynamicTypeEvaluator.Config}. */
         public static final class Builder {
-            private boolean mPlatformDataSourcesInitiallyEnabled = false;
             @Nullable private StateStore mStateStore = null;
             @Nullable private QuotaManager mAnimationQuotaManager = null;
             @Nullable private QuotaManager mDynamicTypesQuotaManager;
@@ -174,19 +166,6 @@
             @Nullable private SensorGateway mSensorGateway = null;
 
             /**
-             * Sets whether sending updates from sensor and time sources should be allowed
-             * initially. After that, enabling updates from sensor and time sources can be done via
-             * {@link #enablePlatformDataSources()} or {@link #disablePlatformDataSources()}.
-             *
-             * <p>Defaults to {@code false}.
-             */
-            @NonNull
-            public Builder setPlatformDataSourcesInitiallyEnabled(boolean value) {
-                mPlatformDataSourcesInitiallyEnabled = value;
-                return this;
-            }
-
-            /**
              * Sets the state store that will be used for dereferencing the state keys in the
              * dynamic types.
              *
@@ -251,7 +230,6 @@
             @NonNull
             public Config build() {
                 return new Config(
-                        mPlatformDataSourcesInitiallyEnabled,
                         mStateStore,
                         mAnimationQuotaManager,
                         mDynamicTypesQuotaManager,
@@ -261,15 +239,6 @@
         }
 
         /**
-         * Gets whether sending updates from sensor and time sources should be allowed initially.
-         * After that, enabling updates from sensor and time sources can be done via {@link
-         * #enablePlatformDataSources()} or {@link #disablePlatformDataSources()}.
-         */
-        public boolean isPlatformDataSourcesInitiallyEnabled() {
-            return mPlatformDataSourcesInitiallyEnabled;
-        }
-
-        /**
          * Gets the state store that will be used for dereferencing the state keys in the dynamic
          * types, or {@code null} which is equivalent to an empty state store (state bindings will
          * trigger {@link DynamicTypeValueReceiver#onInvalidated()}).
@@ -320,7 +289,6 @@
 
     /** Constructs a {@link DynamicTypeEvaluator}. */
     public DynamicTypeEvaluator(@NonNull Config config) {
-        this.mConfig = config;
         this.mStateStore =
                 config.getStateStore() != null ? config.getStateStore() : EMPTY_STATE_STORE;
         this.mAnimationQuotaManager =
@@ -333,21 +301,13 @@
                         : NO_OP_QUOTA_MANAGER;
         Handler uiHandler = new Handler(Looper.getMainLooper());
         MainThreadExecutor uiExecutor = new MainThreadExecutor(uiHandler);
-        this.mTimeGateway =
-                config.getTimeGateway() != null
-                        ? config.getTimeGateway()
-                        : new TimeGatewayImpl(uiHandler);
-        this.mTimeDataSource = new EpochTimePlatformDataSource(uiExecutor, mTimeGateway);
-        if (config.isPlatformDataSourcesInitiallyEnabled()
-                && this.mTimeGateway instanceof TimeGatewayImpl) {
-            ((TimeGatewayImpl) this.mTimeGateway).enableUpdates();
+        TimeGateway timeGateway = config.getTimeGateway();
+        if (timeGateway == null) {
+                timeGateway = new TimeGatewayImpl(uiHandler);
+                ((TimeGatewayImpl) timeGateway).enableUpdates();
         }
+        this.mTimeDataSource = new EpochTimePlatformDataSource(uiExecutor, timeGateway);
         if (config.getSensorGateway() != null) {
-            if (config.isPlatformDataSourcesInitiallyEnabled()) {
-                config.getSensorGateway().enableUpdates();
-            } else {
-                config.getSensorGateway().disableUpdates();
-            }
             this.mSensorGatewayDataSource =
                     new SensorGatewayPlatformDataSource(uiExecutor, config.getSensorGateway());
         } else {
@@ -1102,46 +1062,6 @@
         resultBuilder.add(node);
     }
 
-    /** Enables sending updates on sensor and time. */
-    @UiThread
-    public void enablePlatformDataSources() {
-        if (this.mTimeGateway instanceof TimeGatewayImpl) {
-            ((TimeGatewayImpl) mTimeGateway).enableUpdates();
-        }
-        if (mConfig.getSensorGateway() != null) {
-            mConfig.getSensorGateway().enableUpdates();
-        }
-    }
-
-    /** Disables sending updates on sensor and time. */
-    @UiThread
-    public void disablePlatformDataSources() {
-        if (this.mTimeGateway instanceof TimeGatewayImpl) {
-            ((TimeGatewayImpl) mTimeGateway).disableUpdates();
-        }
-        if (mConfig.getSensorGateway() != null) {
-            mConfig.getSensorGateway().disableUpdates();
-        }
-    }
-
-    /**
-     * Closes resources owned by this {@link DynamicTypeEvaluator}.
-     *
-     * <p>This will not close provided resources, like the {@link TimeGateway} or {@link
-     * SensorGateway}.
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Override
-    public void close() {
-        if (mTimeGateway instanceof TimeGatewayImpl) {
-            try {
-                ((TimeGatewayImpl) mTimeGateway).close();
-            } catch (RuntimeException ex) {
-                Log.e(TAG, "Error while cleaning up time gateway", ex);
-            }
-        }
-    }
-
     /**
      * Wraps {@link DynamicTypeValueReceiver} and executes its methods on the given {@link
      * Executor}.
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
index 6a3f1d3..dbe00c9 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
@@ -60,15 +60,13 @@
     }
 
     /** Returns true if all quota has been released. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public boolean isAllQuotaReleased() {
         return mQuotaCounter == 0;
     }
 
     /** Returns the remaining quota. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public int getRemainingQuota() {
         return mQuotaCap - mQuotaCounter;
     }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
index e30b7c5..67b1243 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
@@ -92,7 +92,6 @@
         StateStore stateStore = new StateStore(new HashMap<>());
         return new DynamicTypeEvaluator(
                 new DynamicTypeEvaluator.Config.Builder()
-                        .setPlatformDataSourcesInitiallyEnabled(true)
                         .setStateStore(stateStore)
                         .setAnimationQuotaManager(animationQuota)
                         .setDynamicTypesQuotaManager(dynamicTypesQuota)
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
index 86a002e..7632f30 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
@@ -326,7 +326,6 @@
         DynamicTypeEvaluator evaluator =
                 new DynamicTypeEvaluator(
                         new DynamicTypeEvaluator.Config.Builder()
-                                .setPlatformDataSourcesInitiallyEnabled(true)
                                 .setStateStore(stateStore)
                                 .setAnimationQuotaManager(new FixedQuotaManagerImpl(MAX_VALUE))
                                 .build());
@@ -491,6 +490,7 @@
         }
 
         @Override
+        @NonNull
         public String toString() {
             return mName + " = " + mExpectedValue;
         }
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
index ba47d07..be3cff9 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
@@ -24,6 +24,8 @@
 import android.annotation.SuppressLint;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.icu.util.ULocale;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -46,6 +48,7 @@
 import androidx.wear.protolayout.expression.pipeline.FixedQuotaManagerImpl;
 import androidx.wear.protolayout.expression.pipeline.QuotaManager;
 import androidx.wear.protolayout.expression.pipeline.StateStore;
+import androidx.wear.protolayout.expression.pipeline.TimeGatewayImpl;
 import androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway;
 import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicBool;
 import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicColor;
@@ -92,16 +95,15 @@
     boolean mFullyVisible;
     @NonNull final QuotaManager mAnimationQuotaManager;
     @NonNull private final DynamicTypeEvaluator mEvaluator;
+    @NonNull private final TimeGatewayImpl mTimeGateway;
 
     /** Creates a {@link ProtoLayoutDynamicDataPipeline} without animation support. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public ProtoLayoutDynamicDataPipeline(
-            boolean canUpdateGateways,
             @Nullable SensorGateway sensorGateway,
             @NonNull StateStore stateStore) {
         // Build pipeline with quota that doesn't allow any animations.
         this(
-                canUpdateGateways,
                 sensorGateway,
                 stateStore,
                 /* enableAnimations= */ false,
@@ -115,13 +117,11 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public ProtoLayoutDynamicDataPipeline(
-            boolean canUpdateGateways,
             @Nullable SensorGateway sensorGateway,
             @NonNull StateStore stateStore,
             @NonNull QuotaManager animationQuotaManager,
             @NonNull QuotaManager dynamicNodesQuotaManager) {
         this(
-                canUpdateGateways,
                 sensorGateway,
                 stateStore,
                 /* enableAnimations= */ true,
@@ -131,7 +131,6 @@
 
     /** Creates a {@link ProtoLayoutDynamicDataPipeline}. */
     private ProtoLayoutDynamicDataPipeline(
-            boolean canUpdateGateways,
             @Nullable SensorGateway sensorGateway,
             @NonNull StateStore stateStore,
             boolean enableAnimations,
@@ -139,9 +138,10 @@
             @NonNull QuotaManager dynamicNodeQuotaManager) {
         this.mEnableAnimations = enableAnimations;
         this.mAnimationQuotaManager = animationQuotaManager;
+        this.mTimeGateway = new TimeGatewayImpl(new Handler(Looper.getMainLooper()));
         DynamicTypeEvaluator.Config.Builder evaluatorConfigBuilder =
                 new DynamicTypeEvaluator.Config.Builder()
-                        .setPlatformDataSourcesInitiallyEnabled(canUpdateGateways)
+                        .setTimeGateway(mTimeGateway)
                         .setStateStore(stateStore);
         evaluatorConfigBuilder.setDynamicTypesQuotaManager(dynamicNodeQuotaManager);
         if (sensorGateway != null) {
@@ -150,12 +150,12 @@
         if (enableAnimations) {
             evaluatorConfigBuilder.setAnimationQuotaManager(animationQuotaManager);
         }
-        this.mEvaluator = new DynamicTypeEvaluator(evaluatorConfigBuilder.build());
+        DynamicTypeEvaluator.Config evaluatorConfig = evaluatorConfigBuilder.build();
+        this.mEvaluator = new DynamicTypeEvaluator(evaluatorConfig);
     }
 
     /** Returns the number of active dynamic types in this pipeline. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public int size() {
         return mPositionIdTree.getAllNodes().stream().mapToInt(NodeInfo::size).sum();
     }
@@ -186,7 +186,6 @@
      */
     @VisibleForTesting
     @NonNull
-    @RestrictTo(Scope.TESTS)
     public PipelineMaker newPipelineMaker() {
         return newPipelineMaker(
                 (enterTransition, view) -> new AnimationSet(/* shareInterpolator= */ false),
@@ -201,10 +200,12 @@
     @SuppressWarnings("RestrictTo")
     @RestrictTo(Scope.LIBRARY_GROUP)
     public void setUpdatesEnabled(boolean canUpdate) {
+        // SensorGateway is not owned by ProtoLayoutDynamicDataPipeline, so the callers who create
+        // it are responsible for updates.
         if (canUpdate) {
-            mEvaluator.enablePlatformDataSources();
+            mTimeGateway.enableUpdates();
         } else {
-            mEvaluator.disablePlatformDataSources();
+            mTimeGateway.disableUpdates();
         }
     }
 
@@ -212,7 +213,8 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     @SuppressWarnings("RestrictTo")
     public void close() {
-        mEvaluator.close();
+        mPositionIdTree.clear();
+        mTimeGateway.close();
     }
 
     /**
@@ -1062,8 +1064,7 @@
     }
 
     /** Returns how many animations are running. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public int getRunningAnimationsCount() {
         return mPositionIdTree.getAllNodes().stream()
                         .mapToInt(NodeInfo::getRunningAnimationCount)
@@ -1077,8 +1078,7 @@
     }
 
     /** Returns How many dynamic data nodes exist in the pipeline. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public int getDynamicExpressionsNodesCount() {
         return mPositionIdTree.getAllNodes().stream()
                 .mapToInt(NodeInfo::getExpressionNodesCount)
@@ -1086,8 +1086,7 @@
     }
 
     /** Returns whether all quota has been released. */
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @RestrictTo(Scope.TESTS)
+    @VisibleForTesting
     public boolean isAllQuotaReleased() {
         return mAnimationQuotaManager instanceof FixedQuotaManagerImpl
                 && ((FixedQuotaManagerImpl) mAnimationQuotaManager).isAllQuotaReleased();
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
index 19fafca..b496675 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
@@ -646,17 +646,15 @@
 
         StateStore stateStore = config.getStateStore();
         if (stateStore != null) {
-            boolean updatesEnabled = config.getUpdatesEnabled();
             mDataPipeline =
                     config.getAnimationEnabled()
                             ? new ProtoLayoutDynamicDataPipeline(
-                                    updatesEnabled,
-                                    config.getSensorGateway(),
+                            config.getSensorGateway(),
                                     stateStore,
                                     new FixedQuotaManagerImpl(config.getRunningAnimationsLimit()),
                                     new FixedQuotaManagerImpl(DYNAMIC_NODES_MAX_COUNT))
                             : new ProtoLayoutDynamicDataPipeline(
-                                    updatesEnabled, config.getSensorGateway(), stateStore);
+                                    config.getSensorGateway(), stateStore);
             mDataPipeline.setFullyVisible(config.getIsViewFullyVisible());
         } else {
             mDataPipeline = null;
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
index c93a489..f8c0a3d 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
@@ -377,7 +377,6 @@
                         .build();
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -444,7 +443,6 @@
         DynamicInt32 dynamicInt = fixedDynamicInt32(1);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -465,7 +463,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -496,7 +493,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -525,7 +521,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -582,7 +577,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -621,7 +615,6 @@
         List<String> expected = Arrays.asList(NODE_1_1, NODE_1_1_1);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -643,7 +636,6 @@
     public void resolvedAnimatedImage_canStorePlayAndResetOnVisible() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -677,7 +669,6 @@
     public void resolvedAnimatedImage_canStoreAndPlayOnVisibleOnce() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -709,7 +700,6 @@
     public void resolvedAnimatedImage_canStorePlayAndResetOnLoad() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -738,7 +728,6 @@
         String boolStateKey = "KEY";
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -776,7 +765,6 @@
         String boolStateKey = "KEY";
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -797,7 +785,6 @@
         String boolStateKey = "KEY";
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -820,7 +807,6 @@
         FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(MAX_VALUE);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -854,7 +840,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -892,7 +877,6 @@
         FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(/* quotaCap= */ 0);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -920,7 +904,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -958,7 +941,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1008,7 +990,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1040,7 +1021,6 @@
         FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(/* quotaCap= */ 0);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1125,7 +1105,6 @@
     public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableFixedFloat() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1158,7 +1137,6 @@
     public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableDynamicFloat() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1204,7 +1182,6 @@
     public void resolvedSeekableAnimatedImage_getSeekableAnimationTotalDurationMillis() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1228,7 +1205,6 @@
     public void whenInvisible_pausesAvds() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1264,7 +1240,6 @@
     public void visibilityChange_avdsStatusChange() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1377,7 +1352,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1431,7 +1405,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1488,7 +1461,6 @@
 
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1546,7 +1518,6 @@
         FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(1);
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1798,7 +1769,6 @@
                 Trigger.newBuilder().setOnLoadTrigger(OnLoadTrigger.getDefaultInstance()).build();
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
                         /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
@@ -1843,13 +1813,11 @@
         ProtoLayoutDynamicDataPipeline pipeline =
                 enableAnimations
                         ? new ProtoLayoutDynamicDataPipeline(
-                                /* canUpdateGateways= */ true,
-                                /* sensorGateway= */ null,
+                        /* sensorGateway= */ null,
                                 mStateStore,
                                 new FixedQuotaManagerImpl(MAX_VALUE),
                                 new FixedQuotaManagerImpl(MAX_VALUE))
                         : new ProtoLayoutDynamicDataPipeline(
-                                /* canUpdateGateways= */ true,
                                 /* sensorGateway= */ null,
                                 mStateStore);
         shadowOf(getMainLooper()).idle();
@@ -1874,8 +1842,7 @@
         AddToListCallback<Float> receiver =
                 new AddToListCallback<>(results, /* invalidList= */ null);
         ProtoLayoutDynamicDataPipeline pipeline =
-                new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+                new ProtoLayoutDynamicDataPipeline(  /* sensorGateway= */ null, mStateStore);
         shadowOf(getMainLooper()).idle();
 
         pipeline.setFullyVisible(true);
@@ -1893,8 +1860,7 @@
         AddToListCallback<Integer> receiver =
                 new AddToListCallback<>(results, /* invalidList= */ null);
         ProtoLayoutDynamicDataPipeline pipeline =
-                new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+                new ProtoLayoutDynamicDataPipeline(  /* sensorGateway= */ null, mStateStore);
         shadowOf(getMainLooper()).idle();
 
         pipeline.setFullyVisible(true);
@@ -1912,8 +1878,7 @@
         AddToListCallback<Float> receiver =
                 new AddToListCallback<>(results, /* invalidList= */ null);
         ProtoLayoutDynamicDataPipeline pipeline =
-                new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+                new ProtoLayoutDynamicDataPipeline(  /* sensorGateway= */ null, mStateStore);
         shadowOf(getMainLooper()).idle();
 
         pipeline.setFullyVisible(true);
@@ -1931,8 +1896,7 @@
         AddToListCallback<Float> receiver =
                 new AddToListCallback<>(results, /* invalidList= */ null);
         ProtoLayoutDynamicDataPipeline pipeline =
-                new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+                new ProtoLayoutDynamicDataPipeline(  /* sensorGateway= */ null, mStateStore);
         shadowOf(getMainLooper()).idle();
 
         pipeline.setFullyVisible(true);
@@ -1950,8 +1914,7 @@
         AddToListCallback<Integer> receiver =
                 new AddToListCallback<>(results, /* invalidList= */ null);
         ProtoLayoutDynamicDataPipeline pipeline =
-                new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+                new ProtoLayoutDynamicDataPipeline(  /* sensorGateway= */ null, mStateStore);
         shadowOf(getMainLooper()).idle();
 
         pipeline.setFullyVisible(true);
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
index 4a49fcd..9e263bf 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
@@ -3378,8 +3378,7 @@
             FixedQuotaManagerImpl quotaManager) {
         mDataPipeline =
                 new ProtoLayoutDynamicDataPipeline(
-                        /* canUpdateGateways= */ true,
-                        null,
+                        /* sensorGateway= */ null,
                         mStateStore,
                         quotaManager,
                         new FixedQuotaManagerImpl(MAX_VALUE));
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index 26dc1e9..daae481 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -35,7 +35,6 @@
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.wear.watchface.complications.data.ComplicationData
-import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.hasExpression
 import androidx.wear.watchface.complications.data.ComplicationType
 import androidx.wear.watchface.complications.data.ComplicationType.Companion.fromWireType
 import androidx.wear.watchface.complications.data.GoalProgressComplicationData
@@ -616,7 +615,7 @@
                 require(complicationData.validTimeRange == TimeRange.ALWAYS) {
                     "Preview data should have time range set to ALWAYS."
                 }
-                require(!hasExpression(complicationData.asWireComplicationData())) {
+                require(!complicationData.asWireComplicationData().hasExpression()) {
                     "Preview data must not have expressions."
                 }
             }
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
index 5463ef96..5423cf2 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
@@ -150,7 +150,8 @@
                                 + "validTimeRange=TimeRange("
                                 + "startDateTimeMillis=-1000000000-01-01T00:00:00Z,"
                                 + " endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z),"
-                                + " dataSource=null, persistencePolicy=0, displayPolicy=0),"
+                                + " dataSource=null, persistencePolicy=0, displayPolicy=0, "
+                                + "fallback=null),"
                                 + " timelineEntries=["
                                 + "TimelineEntry(validity=TimeInterval(start=1970-01-02T03:46:40Z,"
                                 + " end=1970-01-03T07:33:20Z),"
@@ -164,7 +165,8 @@
                                 + "validTimeRange=TimeRange("
                                 + "startDateTimeMillis=-1000000000-01-01T00:00:00Z,"
                                 + " endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z),"
-                                + " dataSource=null, persistencePolicy=0, displayPolicy=0))])");
+                                + " dataSource=null, persistencePolicy=0, displayPolicy=0, "
+                                + "fallback=null))])");
     }
 
     @Test
diff --git a/wear/watchface/watchface-complications-data/api/current.ignore b/wear/watchface/watchface-complications-data/api/current.ignore
index 123515f..aa5a43b 100644
--- a/wear/watchface/watchface-complications-data/api/current.ignore
+++ b/wear/watchface/watchface-complications-data/api/current.ignore
@@ -1,4 +1,20 @@
 // Baseline format: 1.0
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.LongTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder to BuilderT
+
+
 RemovedClass: androidx.wear.watchface.complications.data.DataKt:
     Removed class androidx.wear.watchface.complications.data.DataKt
 RemovedClass: androidx.wear.watchface.complications.data.ImageKt:
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index f36afa5..2992e08 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -113,10 +113,10 @@
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -141,10 +141,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -176,9 +176,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -208,10 +208,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -234,9 +234,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -285,10 +285,10 @@
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -315,10 +315,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -352,9 +352,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -459,11 +459,11 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
index 520f8dc..e6efd13 100644
--- a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
@@ -116,10 +116,10 @@
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -144,10 +144,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -179,9 +179,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -211,10 +211,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -237,9 +237,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -288,10 +288,10 @@
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -318,10 +318,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -355,9 +355,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -462,11 +462,11 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.ignore b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
index d3ed8b1..fa0dab0 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.ignore
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
@@ -1,4 +1,20 @@
 // Baseline format: 1.0
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.LongTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder to BuilderT
+
+
 RemovedClass: androidx.wear.watchface.complications.data.DataKt:
     Removed class androidx.wear.watchface.complications.data.DataKt
 RemovedClass: androidx.wear.watchface.complications.data.DefaultComplicationDataSourcePolicyWireFormat:
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index a60802c..7a434b5 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -113,10 +113,10 @@
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -141,10 +141,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -176,9 +176,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -208,10 +208,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -234,9 +234,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -286,10 +286,10 @@
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -316,10 +316,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -353,9 +353,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -462,11 +462,11 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public final T setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
index b122f85..4bdd46d 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
@@ -1115,6 +1115,15 @@
     val endDateTimeMillis: Long
         get() = fields.getLong(FIELD_END_TIME, Long.MAX_VALUE)
 
+    /** Returns `true` if the complication contains an expression that needs to be evaluated. */
+    fun hasExpression(): Boolean =
+        (hasRangedValueExpression() && rangedValueExpression != null) ||
+            (hasLongText() && longText?.expression != null) ||
+            (hasLongTitle() && longTitle?.expression != null) ||
+            (hasShortText() && shortText?.expression != null) ||
+            (hasShortTitle() && shortTitle?.expression != null) ||
+            (hasContentDescription() && contentDescription?.expression != null)
+
     /**
      * Returns true if the complication data contains at least one text field with a value that may
      * change based on the current time.
@@ -2230,6 +2239,9 @@
                 FIELD_END_TIME,
                 FIELD_TIMELINE_ENTRIES,
                 FIELD_TIMELINE_ENTRY_TYPE,
+                // Placeholder or fallback.
+                FIELD_PLACEHOLDER_FIELDS,
+                FIELD_PLACEHOLDER_TYPE,
             )
 
         // Used for validation. OPTIONAL_FIELDS[i] is a list containing all the fields which are
@@ -2313,8 +2325,6 @@
                         FIELD_LONG_TEXT,
                         FIELD_MAX_VALUE,
                         FIELD_MIN_VALUE,
-                        FIELD_PLACEHOLDER_FIELDS,
-                        FIELD_PLACEHOLDER_TYPE,
                         FIELD_SMALL_IMAGE,
                         FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
                         FIELD_SHORT_TITLE,
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
index d6589c5..89fbc08 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
@@ -216,7 +216,6 @@
             evaluator =
                 DynamicTypeEvaluator(
                     DynamicTypeEvaluator.Config.Builder()
-                        .setPlatformDataSourcesInitiallyEnabled(true)
                         .apply { stateStore?.let { setStateStore(it) } }
                         .apply { timeGateway?.let { setTimeGateway(it) } }
                         .apply { sensorGateway?.let { setSensorGateway(it) } }
@@ -243,7 +242,6 @@
                 for (receiver in pendingReceivers + invalidReceivers + completeReceivers) {
                     receiver.close()
                 }
-                if (this@State::evaluator.isInitialized) evaluator.close()
             }
         }
 
@@ -304,16 +302,6 @@
 
     companion object {
         val INVALID_DATA: WireComplicationData = NoDataComplicationData().asWireComplicationData()
-
-        fun hasExpression(data: WireComplicationData): Boolean =
-            data.run {
-                (hasRangedValueExpression() && rangedValueExpression != null) ||
-                    (hasLongText() && longText?.expression != null) ||
-                    (hasLongTitle() && longTitle?.expression != null) ||
-                    (hasShortText() && shortText?.expression != null) ||
-                    (hasShortTitle() && shortTitle?.expression != null) ||
-                    (hasContentDescription() && contentDescription?.expression != null)
-            }
     }
 }
 
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index 42fe7ce..e34d53c 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ComplicationExperimental::class)
-
 package androidx.wear.watchface.complications.data
 
 import android.app.PendingIntent
@@ -115,7 +113,8 @@
     public val validTimeRange: TimeRange = TimeRange.ALWAYS,
     public val dataSource: ComponentName?,
     @ComplicationPersistencePolicy public val persistencePolicy: Int,
-    @ComplicationDisplayPolicy public val displayPolicy: Int
+    @ComplicationDisplayPolicy public val displayPolicy: Int,
+    private val fallback: ComplicationData?,
 ) {
     /**
      * [tapAction] which is a [PendingIntent] unfortunately can't be serialized. This property is
@@ -132,7 +131,6 @@
      * Converts this value to [WireComplicationData] object used for serialization.
      *
      * This is only needed internally to convert to the underlying communication protocol.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun asWireComplicationData(): WireComplicationData {
@@ -153,6 +151,13 @@
         builder.setDataSource(dataSource)
         builder.setPersistencePolicy(persistencePolicy)
         builder.setDisplayPolicy(displayPolicy)
+        if (fallback == null) {
+            builder.setPlaceholder(null)
+        } else {
+            val placeholderBuilder = fallback.createWireComplicationDataBuilder()
+            fallback.fillWireComplicationDataBuilder(placeholderBuilder)
+            builder.setPlaceholder(placeholderBuilder.build())
+        }
     }
 
     /**
@@ -186,24 +191,14 @@
 
     override fun hashCode(): Int = asWireComplicationData().hashCode()
 
-    /**
-     * Builder for properties in common for most Complication Types.
-     *
-     */
+    /** Builder for properties in common for most Complication Types. */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public abstract class BaseBuilder<T : BaseBuilder<T, ReturnT>, ReturnT> {
+    public sealed class BaseBuilder<BuilderT : BaseBuilder<BuilderT, BuiltT>, BuiltT> {
         internal var cachedWireComplicationData: WireComplicationData? = null
         internal var dataSource: ComponentName? = null
         internal var persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED
         internal var displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
-
-        @Suppress("NewApi")
-        internal fun setCommon(data: WireComplicationData) = apply {
-            setCachedWireComplicationData(data)
-            setDataSource(data.dataSource)
-            setPersistencePolicy(data.persistencePolicy)
-            setDisplayPolicy(data.displayPolicy)
-        }
+        internal var fallback: BuiltT? = null
 
         /**
          * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
@@ -213,37 +208,48 @@
          * set this value on its behalf.
          */
         @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
-        public fun setDataSource(dataSource: ComponentName?): T {
+        public fun setDataSource(dataSource: ComponentName?): BuilderT {
             this.dataSource = dataSource
-            return this as T
+            return this as BuilderT
         }
 
         @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
         internal fun setCachedWireComplicationData(
             cachedWireComplicationData: WireComplicationData?
-        ): T {
+        ): BuilderT {
             this.cachedWireComplicationData = cachedWireComplicationData
-            return this as T
+            return this as BuilderT
         }
 
         /** Sets the complication's [ComplicationPersistencePolicy]. */
         @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        public fun setPersistencePolicy(@ComplicationPersistencePolicy persistencePolicy: Int): T {
+        public fun setPersistencePolicy(
+            @ComplicationPersistencePolicy persistencePolicy: Int
+        ): BuilderT {
             this.persistencePolicy = persistencePolicy
-            return this as T
+            return this as BuilderT
         }
 
         /** Sets the complication's [ComplicationDisplayPolicy]. */
         @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        public fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int): T {
+        public fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int): BuilderT {
             this.displayPolicy = displayPolicy
-            return this as T
+            return this as BuilderT
+        }
+
+        /** Sets the complication's fallback, use in case any expression has been invalidated. */
+        // TODO(b/269414040): Unhide complication expression APIs.
+        @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public fun setFallback(fallback: BuiltT?): BuilderT {
+            this.fallback = fallback
+            return this as BuilderT
         }
 
         /** Builds the ComplicationData */
-        abstract fun build(): ReturnT
+        abstract fun build(): BuiltT
     }
 }
 
@@ -277,7 +283,8 @@
         dataSource = null,
         persistencePolicy = placeholder?.persistencePolicy
                 ?: ComplicationPersistencePolicies.CACHING_ALLOWED,
-        displayPolicy = placeholder?.displayPolicy ?: ComplicationDisplayPolicies.ALWAYS_DISPLAY
+        displayPolicy = placeholder?.displayPolicy ?: ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+        fallback = placeholder,
     ) {
 
     /** Constructs a NoDataComplicationData without a [placeholder]. */
@@ -303,17 +310,6 @@
             else -> null
         }
 
-    override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
-        super.fillWireComplicationDataBuilder(builder)
-        if (placeholder == null) {
-            builder.setPlaceholder(null)
-        } else {
-            val placeholderBuilder = placeholder.createWireComplicationDataBuilder()
-            placeholder.fillWireComplicationDataBuilder(placeholderBuilder)
-            builder.setPlaceholder(placeholderBuilder.build())
-        }
-    }
-
     override fun toString(): String {
         return "NoDataComplicationData(" +
             "placeholder=$placeholder, " +
@@ -341,7 +337,8 @@
         cachedWireComplicationData = null,
         dataSource = null,
         persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
-        displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
+        displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+        fallback = null,
     ) {
     // Always empty.
     override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {}
@@ -370,7 +367,8 @@
         cachedWireComplicationData = null,
         dataSource = null,
         persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
-        displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
+        displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+        fallback = null,
     ) {
     // Always empty.
     override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {}
@@ -400,9 +398,8 @@
  * recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
  * specify both a [monochromaticImage] and a [smallImage]
  *
- * A data source that wants to serve a ShortTextComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a ShortTextComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="SHORT_TEXT"/>
@@ -434,6 +431,7 @@
  *   be rendered as a light grey box.
  * @property contentDescription The content description field for accessibility. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class ShortTextComplicationData
 internal constructor(
@@ -447,7 +445,9 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public val fallback: ShortTextComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -456,7 +456,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Builder for [ShortTextComplicationData].
@@ -465,9 +466,9 @@
      *
      * @param text The main localized [ComplicationText]. This must be less than 7 characters long
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications do
-     * not have textual representation this attribute can be used for providing such. Please do not
-     * include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     public class Builder(
         private val text: ComplicationText,
@@ -516,7 +517,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
     }
 
@@ -543,7 +545,8 @@
             "contentDescription=$contentDescription, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() =
@@ -590,9 +593,8 @@
  * recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
  * specify both a [monochromaticImage] and a [smallImage].
  *
- * A data source that wants to serve a LongTextComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a LongTextComplicationData must include the following meta data
+ * in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="LONG_TEXT"/>
@@ -614,6 +616,7 @@
  *   be rendered as a light grey box.
  * @property contentDescription The content description field for accessibility. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class LongTextComplicationData
 internal constructor(
@@ -627,7 +630,9 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public val fallback: LongTextComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -636,7 +641,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Builder for [LongTextComplicationData].
@@ -646,9 +652,9 @@
      * @param text Localized main [ComplicationText] to display within the complication. There isn't
      *   an explicit character limit but text may be truncated if too long
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications do
-     * not have textual representation this attribute can be used for providing such. Please do not
-     * include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     public class Builder(
         private val text: ComplicationText,
@@ -697,7 +703,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
     }
 
@@ -724,7 +731,8 @@
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() =
@@ -820,9 +828,8 @@
  * recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
  * specify both a [monochromaticImage] and a [smallImage].
  *
- * A data source that wants to serve a RangedValueComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a RangedValueComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="GOAL_PROGRESS"/>
@@ -865,6 +872,7 @@
  * @property valueType The semantic meaning of [value]. The complication renderer may choose to
  *   visually differentiate between the different types, for example rendering a dot on a line/arc
  *   to indicate the value for a [TYPE_RATING].
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class RangedValueComplicationData
 internal constructor(
@@ -884,7 +892,10 @@
     public val colorRamp: ColorRamp?,
     @RangedValueType public val valueType: Int,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: RangedValueComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -893,15 +904,15 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * The [DynamicFloat] optionally set by the data source. If present the system will dynamically
      * evaluate this and store the result in [value]. Watch faces can typically ignore this field.
-     *
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public val valueExpression: DynamicFloat? = valueExpression
 
     @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -933,9 +944,9 @@
          * @param max The maximum value. This must be less than [Float.MAX_VALUE]. For
          *   [TYPE_PERCENTAGE] this must be 0f.
          * @param contentDescription Defines localized text that briefly describes content of the
-         * complication. This property is used primarily for accessibility. Since some complications
-         * do not have textual representation this attribute can be used for providing such. Please
-         * do not include the word 'complication' in the description.
+         *   complication. This property is used primarily for accessibility. Since some
+         *   complications do not have textual representation this attribute can be used for
+         *   providing such. Please do not include the word 'complication' in the description.
          */
         public constructor(
             value: Float,
@@ -954,10 +965,11 @@
          * @param max The maximum value. This must be less than [Float.MAX_VALUE]. For
          *   [TYPE_PERCENTAGE] this must be 0f.
          * @param contentDescription Defines localized text that briefly describes content of the
-         * complication. This property is used primarily for accessibility. Since some complications
-         * do not have textual representation this attribute can be used for providing such. Please
-         * do not include the word 'complication' in the description.
+         *   complication. This property is used primarily for accessibility. Since some
+         *   complications do not have textual representation this attribute can be used for
+         *   providing such. Please do not include the word 'complication' in the description.
          */
+        // TODO(b/269414040): Unhide complication expression APIs.
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public constructor(
             valueExpression: DynamicFloat,
@@ -1058,7 +1070,8 @@
                 colorRamp,
                 valueType,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
         }
     }
@@ -1109,7 +1122,7 @@
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
             "colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
-            "displayPolicy=$displayPolicy)"
+            "displayPolicy=$displayPolicy, fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() =
@@ -1186,9 +1199,8 @@
  * [RangedValueComplicationData.TYPE_RATING] into
  * [RangedValueComplicationData.Builder.setValueType].
  *
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *        android:value="GOAL_PROGRESS"/>
@@ -1227,6 +1239,7 @@
  *   include the word 'complication' in the description.
  * @property colorRamp Optional hint to render the progress bar representing [value] with the
  *   specified [ColorRamp].
+ * @property fallback Used in case any expression has been invalidated.
  */
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class GoalProgressComplicationData
@@ -1245,7 +1258,10 @@
     dataSource: ComponentName?,
     public val colorRamp: ColorRamp?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: GoalProgressComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -1254,15 +1270,15 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * The [DynamicFloat] optionally set by the data source. If present the system will dynamically
      * evaluate this and store the result in [value]. Watch faces can typically ignore this field.
-     *
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public val valueExpression: DynamicFloat? = valueExpression
 
     /**
@@ -1287,9 +1303,9 @@
          * @param value The value of the goal complication which should be >= 0.
          * @param targetValue The target value. This must be less than [Float.MAX_VALUE].
          * @param contentDescription Defines localized text that briefly describes content of the
-         * complication. This property is used primarily for accessibility. Since some complications
-         * do not have textual representation this attribute can be used for providing such. Please
-         * do not include the word 'complication' in the description.
+         *   complication. This property is used primarily for accessibility. Since some
+         *   complications do not have textual representation this attribute can be used for
+         *   providing such. Please do not include the word 'complication' in the description.
          */
         public constructor(
             value: Float,
@@ -1304,10 +1320,11 @@
          *   evaluated into a value dynamically, and should be >= 0.
          * @param targetValue The target value. This must be less than [Float.MAX_VALUE].
          * @param contentDescription Defines localized text that briefly describes content of the
-         * complication. This property is used primarily for accessibility. Since some complications
-         * do not have textual representation this attribute can be used for providing such. Please
-         * do not include the word 'complication' in the description.
+         *   complication. This property is used primarily for accessibility. Since some
+         *   complications do not have textual representation this attribute can be used for
+         *   providing such. Please do not include the word 'complication' in the description.
          */
+        // TODO(b/269414040): Unhide complication expression APIs.
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public constructor(
             valueExpression: DynamicFloat,
@@ -1392,6 +1409,7 @@
                 colorRamp,
                 persistencePolicy,
                 displayPolicy,
+                fallback = fallback,
             )
         }
     }
@@ -1440,7 +1458,7 @@
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
             "colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
-            "displayPolicy=$displayPolicy)"
+            "displayPolicy=$displayPolicy, fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() =
@@ -1493,9 +1511,8 @@
  * recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
  * specify both a [monochromaticImage] and a [smallImage].
  *
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="WEIGHTED_ELEMENTS"/>
@@ -1536,6 +1553,7 @@
  *   grey box.
  * @property contentDescription The content description field for accessibility. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class WeightedElementsComplicationData
@@ -1552,7 +1570,10 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: WeightedElementsComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -1561,7 +1582,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Describes a single value within a [WeightedElementsComplicationData].
@@ -1617,9 +1639,9 @@
      *   to an experience where the color key becomes obvious. The maximum valid size of this list
      *   is provided by [getMaxElements].
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications
-     * do not have textual representation this attribute can be used for providing such. Please
-     * do not include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     public class Builder(
@@ -1707,7 +1729,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
         }
     }
@@ -1755,7 +1778,8 @@
             "text=$text, contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() =
@@ -1795,7 +1819,6 @@
  *
  * A data source that wants to serve a MonochromaticImageComplicationData must include the following
  * meta data in its manifest (NB the value is a comma separated list):
- *
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="ICON"/>
@@ -1810,6 +1833,7 @@
  *   any information to the user, then provide an empty content description. If no content
  *   description is provided, a generic content description will be used instead. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class MonochromaticImageComplicationData
 internal constructor(
@@ -1820,7 +1844,10 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: MonochromaticImageComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -1829,7 +1856,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Builder for [MonochromaticImageComplicationData].
@@ -1838,9 +1866,9 @@
      *
      * @param monochromaticImage The [MonochromaticImage] to be displayed
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications
-     * do not have textual representation this attribute can be used for providing such. Please
-     * do not include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     public class Builder(
         private val monochromaticImage: MonochromaticImage,
@@ -1870,7 +1898,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
     }
 
@@ -1895,7 +1924,8 @@
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     /** @hide */
@@ -1910,9 +1940,8 @@
  *
  * The image is expected to always be displayed.
  *
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="SMALL_IMAGE"/>
@@ -1927,6 +1956,7 @@
  *   any information to the user, then provide an empty content description. If no content
  *   description is provided, a generic content description will be used instead. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class SmallImageComplicationData
 internal constructor(
@@ -1937,7 +1967,10 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: SmallImageComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -1946,7 +1979,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Builder for [SmallImageComplicationData].
@@ -1955,9 +1989,9 @@
      *
      * @param smallImage The [SmallImage] to be displayed
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications
-     * do not have textual representation this attribute can be used for providing such. Please
-     * do not include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     public class Builder(
         private val smallImage: SmallImage,
@@ -1987,7 +2021,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
     }
 
@@ -2010,7 +2045,8 @@
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() = smallImage.isPlaceholder()
@@ -2031,9 +2067,8 @@
  * part of the watch face or within a complication. The image is large enough to be cover the entire
  * screen. The image may be cropped to fit the watch face or complication.
  *
- * A data source that wants to serve a PhotoImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a PhotoImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
  * ```
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  *    android:value="LARGE_IMAGE"/>
@@ -2049,6 +2084,7 @@
  *   any information to the user, then provide an empty content description. If no content
  *   description is provided, a generic content description will be used instead. Please do not
  *   include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
  */
 public class PhotoImageComplicationData
 internal constructor(
@@ -2059,7 +2095,10 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
+    // TODO(b/269414040): Unhide complication expression APIs.
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public val fallback: PhotoImageComplicationData?,
 ) :
     ComplicationData(
         TYPE,
@@ -2068,7 +2107,8 @@
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = fallback,
     ) {
     /**
      * Builder for [PhotoImageComplicationData].
@@ -2077,9 +2117,9 @@
      *
      * @param photoImage The [Icon] to be displayed
      * @param contentDescription Defines localized text that briefly describes content of the
-     * complication. This property is used primarily for accessibility. Since some complications
-     * do not have textual representation this attribute can be used for providing such. Please
-     * do not include the word 'complication' in the description.
+     *   complication. This property is used primarily for accessibility. Since some complications
+     *   do not have textual representation this attribute can be used for providing such. Please do
+     *   not include the word 'complication' in the description.
      */
     public class Builder(
         private val photoImage: Icon,
@@ -2110,7 +2150,8 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
+                fallback,
             )
     }
 
@@ -2133,7 +2174,8 @@
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+            "fallback=$fallback)"
     }
 
     override fun hasPlaceholderFields() = photoImage.isPlaceholder()
@@ -2191,7 +2233,7 @@
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
     @ComplicationPersistencePolicy persistencePolicy: Int,
-    @ComplicationDisplayPolicy displayPolicy: Int
+    @ComplicationDisplayPolicy displayPolicy: Int,
 ) :
     ComplicationData(
         TYPE,
@@ -2199,7 +2241,8 @@
         cachedWireComplicationData = cachedWireComplicationData,
         dataSource = dataSource,
         persistencePolicy = persistencePolicy,
-        displayPolicy = displayPolicy
+        displayPolicy = displayPolicy,
+        fallback = null,
     ) {
     /** Builder for [NoPermissionComplicationData]. */
     public class Builder : BaseBuilder<Builder, NoPermissionComplicationData>() {
@@ -2234,7 +2277,7 @@
                 cachedWireComplicationData,
                 dataSource,
                 persistencePolicy,
-                displayPolicy
+                displayPolicy,
             )
     }
 
@@ -2271,305 +2314,167 @@
     }
 }
 
-@Suppress("NewApi")
-internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? {
-    try {
-        // Make sure we use the correct dataSource, persistencePolicy & displayPolicy.
-        val dataSourceCopy = dataSource
-        val persistencePolicyCopy = persistencePolicy
-        val displayPolicyCopy = displayPolicy
-        return when (type) {
-            NoDataComplicationData.TYPE.toWireComplicationType() -> null
-            ShortTextComplicationData.TYPE.toWireComplicationType() -> {
-                ShortTextComplicationData.Builder(
-                        shortText!!.toApiComplicationTextPlaceholderAware(),
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIconPlaceholderAware())
-                        setSmallImage(parseSmallImagePlaceholderAware())
-                        setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-                        setDataSource(dataSourceCopy)
-                        setPersistencePolicy(persistencePolicyCopy)
-                        setDisplayPolicy(displayPolicyCopy)
-                    }
-                    .build()
+internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? =
+    when (type) {
+        NoDataComplicationData.TYPE.toWireComplicationType() -> null
+        EmptyComplicationData.TYPE.toWireComplicationType() -> null
+        NotConfiguredComplicationData.TYPE.toWireComplicationType() -> null
+        else ->
+            toApiComplicationData(placeholderAware = true).let {
+                if (it is NoDataComplicationData) null else it
             }
-            LongTextComplicationData.TYPE.toWireComplicationType() -> {
-                LongTextComplicationData.Builder(
-                        longText!!.toApiComplicationTextPlaceholderAware(),
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIconPlaceholderAware())
-                        setSmallImage(parseSmallImagePlaceholderAware())
-                        setTitle(longTitle?.toApiComplicationTextPlaceholderAware())
-                        setDataSource(dataSourceCopy)
-                        setPersistencePolicy(persistencePolicyCopy)
-                        setDisplayPolicy(displayPolicyCopy)
-                    }
-                    .build()
-            }
-            RangedValueComplicationData.TYPE.toWireComplicationType() ->
-                RangedValueComplicationData.Builder(
-                        value = rangedValue,
-                        valueExpression = rangedValueExpression,
-                        min = rangedMinValue,
-                        max = rangedMaxValue,
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIconPlaceholderAware())
-                        setSmallImage(parseSmallImagePlaceholderAware())
-                        setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-                        setText(shortText?.toApiComplicationTextPlaceholderAware())
-                        setDataSource(dataSourceCopy)
-                        colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
-                        setPersistencePolicy(persistencePolicyCopy)
-                        setDisplayPolicy(displayPolicyCopy)
-                        setValueType(rangedValueType)
-                    }
-                    .build()
-            MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
-                MonochromaticImageComplicationData(
-                    parseIconPlaceholderAware()!!,
-                    contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-                    tapAction,
-                    parseTimeRange(),
-                    this,
-                    dataSourceCopy,
-                    persistencePolicyCopy,
-                    displayPolicyCopy
-                )
-            SmallImageComplicationData.TYPE.toWireComplicationType() ->
-                SmallImageComplicationData(
-                    parseSmallImagePlaceholderAware()!!,
-                    contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-                    tapAction,
-                    parseTimeRange(),
-                    this,
-                    dataSourceCopy,
-                    persistencePolicyCopy,
-                    displayPolicyCopy
-                )
-            PhotoImageComplicationData.TYPE.toWireComplicationType() ->
-                PhotoImageComplicationData(
-                    parseLargeImagePlaceholderAware()!!,
-                    contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-                    tapAction,
-                    parseTimeRange(),
-                    this,
-                    dataSourceCopy,
-                    persistencePolicyCopy,
-                    displayPolicyCopy
-                )
-            GoalProgressComplicationData.TYPE.toWireComplicationType() ->
-                GoalProgressComplicationData.Builder(
-                        value = rangedValue,
-                        valueExpression = rangedValueExpression,
-                        targetValue = targetValue,
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIconPlaceholderAware())
-                        setSmallImage(parseSmallImagePlaceholderAware())
-                        setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-                        setText(shortText?.toApiComplicationTextPlaceholderAware())
-                        setDataSource(dataSourceCopy)
-                        colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
-                        setPersistencePolicy(persistencePolicyCopy)
-                        setDisplayPolicy(displayPolicyCopy)
-                    }
-                    .build()
-            WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
-                WeightedElementsComplicationData.Builder(
-                        elements =
-                            if (elementWeights!!.isEmpty()) {
-                                WeightedElementsComplicationData.PLACEHOLDER
-                            } else {
-                                val elementWeights = this.elementWeights!!
-                                val elementColors = this.elementColors!!
-                                require(elementWeights.size == elementColors.size) {
-                                    "elementWeights and elementColors must have the same size"
-                                }
-                                elementWeights
-                                    .mapIndexed { index, weight ->
-                                        WeightedElementsComplicationData.Element(
-                                            weight,
-                                            elementColors[index]
-                                        )
-                                    }
-                                    .toList()
-                            },
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setElementBackgroundColor(elementBackgroundColor)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIconPlaceholderAware())
-                        setSmallImage(parseSmallImagePlaceholderAware())
-                        setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-                        setText(shortText?.toApiComplicationTextPlaceholderAware())
-                        setDataSource(dataSourceCopy)
-                        setPersistencePolicy(persistencePolicyCopy)
-                        setDisplayPolicy(displayPolicyCopy)
-                    }
-                    .build()
-            else -> null
-        }
-    } catch (e: Exception) {
-        Log.e(
-            TAG,
-            "WireComplicationData.toPlaceholderComplicationData failed for " +
-                toStringNoRedaction(),
-            e
-        )
-        throw e
     }
-}
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public fun WireComplicationData.toApiComplicationData(): ComplicationData =
+    toApiComplicationData(placeholderAware = false)
+
 @Suppress("NewApi")
-public fun WireComplicationData.toApiComplicationData(): ComplicationData {
+private fun WireComplicationData.toApiComplicationData(
+    placeholderAware: Boolean
+): ComplicationData {
     try {
         return when (type) {
-            NoDataComplicationData.TYPE.toWireComplicationType() -> {
-                placeholder?.toPlaceholderComplicationData()?.let {
-                    NoDataComplicationData(it, this@toApiComplicationData)
-                }
-                    ?: NoDataComplicationData(null, this@toApiComplicationData)
-            }
+            NoDataComplicationData.TYPE.toWireComplicationType() ->
+                NoDataComplicationData(placeholder?.toPlaceholderComplicationData(), this)
             EmptyComplicationData.TYPE.toWireComplicationType() -> EmptyComplicationData()
             NotConfiguredComplicationData.TYPE.toWireComplicationType() ->
                 NotConfiguredComplicationData()
             ShortTextComplicationData.TYPE.toWireComplicationType() ->
-                ShortTextComplicationData.Builder(
-                        shortText!!.toApiComplicationText(),
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setTitle(shortTitle?.toApiComplicationText())
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                    }
-                    .build()
+                ShortTextComplicationData(
+                    text = shortText!!.toApiComplicationText(placeholderAware),
+                    title = shortTitle?.toApiComplicationText(placeholderAware),
+                    monochromaticImage = parseIcon(placeholderAware),
+                    smallImage = parseSmallImage(placeholderAware),
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             LongTextComplicationData.TYPE.toWireComplicationType() ->
-                LongTextComplicationData.Builder(
-                        longText!!.toApiComplicationText(),
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setTitle(longTitle?.toApiComplicationText())
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                    }
-                    .build()
+                LongTextComplicationData(
+                    text = longText!!.toApiComplicationText(placeholderAware),
+                    title = longTitle?.toApiComplicationText(placeholderAware),
+                    monochromaticImage = parseIcon(placeholderAware),
+                    smallImage = parseSmallImage(placeholderAware),
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             RangedValueComplicationData.TYPE.toWireComplicationType() ->
-                RangedValueComplicationData.Builder(
-                        value = rangedValue,
-                        valueExpression = rangedValueExpression,
-                        min = rangedMinValue,
-                        max = rangedMaxValue,
-                        contentDescription = contentDescription?.toApiComplicationText()
-                                ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                        setTitle(shortTitle?.toApiComplicationText())
-                        setText(shortText?.toApiComplicationText())
-                        colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
-                        setValueType(rangedValueType)
-                    }
-                    .build()
+                RangedValueComplicationData(
+                    value = rangedValue,
+                    valueExpression = rangedValueExpression,
+                    min = rangedMinValue,
+                    max = rangedMaxValue,
+                    monochromaticImage = parseIcon(placeholderAware),
+                    smallImage = parseSmallImage(placeholderAware),
+                    title = shortTitle?.toApiComplicationText(placeholderAware),
+                    text = shortText?.toApiComplicationText(placeholderAware),
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    colorRamp = colorRamp?.let { ColorRamp(it, isColorRampInterpolated!!) },
+                    valueType = rangedValueType,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
-                MonochromaticImageComplicationData.Builder(
-                        parseIcon()!!,
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                    }
-                    .build()
+                MonochromaticImageComplicationData(
+                    monochromaticImage = parseIcon(placeholderAware)!!,
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             SmallImageComplicationData.TYPE.toWireComplicationType() ->
-                SmallImageComplicationData.Builder(
-                        parseSmallImage()!!,
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                    }
-                    .build()
+                SmallImageComplicationData(
+                    smallImage = parseSmallImage(placeholderAware)!!,
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             PhotoImageComplicationData.TYPE.toWireComplicationType() ->
-                PhotoImageComplicationData.Builder(
-                        largeImage!!,
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                    }
-                    .build()
+                PhotoImageComplicationData(
+                    photoImage = parseLargeImage(placeholderAware)!!,
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             NoPermissionComplicationData.TYPE.toWireComplicationType() ->
-                NoPermissionComplicationData.Builder()
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                        setTitle(shortTitle?.toApiComplicationText())
-                        setText(shortText?.toApiComplicationText())
-                    }
-                    .build()
+                NoPermissionComplicationData(
+                    text = shortText?.toApiComplicationText(),
+                    title = shortTitle?.toApiComplicationText(),
+                    monochromaticImage = parseIcon(),
+                    smallImage = parseSmallImage(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                )
             GoalProgressComplicationData.TYPE.toWireComplicationType() ->
-                GoalProgressComplicationData.Builder(
-                        value = rangedValue,
-                        valueExpression = rangedValueExpression,
-                        targetValue = targetValue,
-                        contentDescription = contentDescription?.toApiComplicationText()
-                                ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                        setTitle(shortTitle?.toApiComplicationText())
-                        setText(shortText?.toApiComplicationText())
-                        colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
-                    }
-                    .build()
-            WeightedElementsComplicationData.TYPE.toWireComplicationType() -> {
-                val elementWeights = this.elementWeights!!
-                val elementColors = this.elementColors!!
-                require(elementWeights.size == elementColors.size) {
-                    "elementWeights and elementColors must have the same size"
-                }
-                WeightedElementsComplicationData.Builder(
-                        elements =
+                GoalProgressComplicationData(
+                    value = rangedValue,
+                    valueExpression = rangedValueExpression,
+                    targetValue = targetValue,
+                    monochromaticImage = parseIcon(placeholderAware),
+                    smallImage = parseSmallImage(placeholderAware),
+                    title = shortTitle?.toApiComplicationText(placeholderAware),
+                    text = shortText?.toApiComplicationText(placeholderAware),
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    colorRamp = colorRamp?.let { ColorRamp(it, isColorRampInterpolated!!) },
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
+            WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
+                WeightedElementsComplicationData(
+                    elements =
+                        if (placeholderAware && elementWeights!!.isEmpty()) {
+                            WeightedElementsComplicationData.PLACEHOLDER
+                        } else {
+                            val elementWeights = this.elementWeights!!
+                            val elementColors = this.elementColors!!
+                            require(elementWeights.size == elementColors.size) {
+                                "elementWeights and elementColors must have the same size"
+                            }
                             elementWeights
                                 .mapIndexed { index, weight ->
                                     WeightedElementsComplicationData.Element(
@@ -2577,21 +2482,23 @@
                                         elementColors[index]
                                     )
                                 }
-                                .toList(),
-                        contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-                    )
-                    .apply {
-                        setCommon(this@toApiComplicationData)
-                        setElementBackgroundColor(elementBackgroundColor)
-                        setTapAction(tapAction)
-                        setValidTimeRange(parseTimeRange())
-                        setMonochromaticImage(parseIcon())
-                        setSmallImage(parseSmallImage())
-                        setTitle(shortTitle?.toApiComplicationText())
-                        setText(shortText?.toApiComplicationText())
-                    }
-                    .build()
-            }
+                                .toList()
+                        },
+                    elementBackgroundColor = elementBackgroundColor,
+                    monochromaticImage = parseIcon(placeholderAware),
+                    smallImage = parseSmallImage(placeholderAware),
+                    title = shortTitle?.toApiComplicationText(placeholderAware),
+                    text = shortText?.toApiComplicationText(placeholderAware),
+                    contentDescription = contentDescription?.toApiComplicationText()
+                            ?: ComplicationText.EMPTY,
+                    tapAction = tapAction,
+                    validTimeRange = parseTimeRange(),
+                    cachedWireComplicationData = this,
+                    dataSource = dataSource,
+                    persistencePolicy = persistencePolicy,
+                    displayPolicy = displayPolicy,
+                    fallback = placeholder?.toTypedApiComplicationData(),
+                )
             else -> NoDataComplicationData()
         }
     } catch (e: Exception) {
@@ -2604,6 +2511,11 @@
     }
 }
 
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Suppress("UNCHECKED_CAST")
+public fun <T : ComplicationData> WireComplicationData.toTypedApiComplicationData(): T =
+    toApiComplicationData() as T
+
 private fun WireComplicationData.parseTimeRange() =
     if ((startDateTimeMillis == 0L) and (endDateTimeMillis == Long.MAX_VALUE)) {
         null
@@ -2614,36 +2526,18 @@
         )
     }
 
-private fun WireComplicationData.parseIcon() =
+private fun WireComplicationData.parseIcon(placeholderAware: Boolean = false) =
     icon?.let {
-        MonochromaticImage.Builder(it).apply { setAmbientImage(burnInProtectionIcon) }.build()
-    }
-
-private fun WireComplicationData.parseIconPlaceholderAware() =
-    icon?.let {
-        if (it.isPlaceholder()) {
+        if (placeholderAware && it.isPlaceholder()) {
             MonochromaticImage.PLACEHOLDER
         } else {
             MonochromaticImage.Builder(it).apply { setAmbientImage(burnInProtectionIcon) }.build()
         }
     }
 
-private fun WireComplicationData.parseSmallImage() =
+private fun WireComplicationData.parseSmallImage(placeholderAware: Boolean = false) =
     smallImage?.let {
-        val imageStyle =
-            when (smallImageStyle) {
-                WireComplicationData.IMAGE_STYLE_ICON -> SmallImageType.ICON
-                WireComplicationData.IMAGE_STYLE_PHOTO -> SmallImageType.PHOTO
-                else -> SmallImageType.PHOTO
-            }
-        SmallImage.Builder(it, imageStyle)
-            .apply { setAmbientImage(burnInProtectionSmallImage) }
-            .build()
-    }
-
-private fun WireComplicationData.parseSmallImagePlaceholderAware() =
-    smallImage?.let {
-        if (it.isPlaceholder()) {
+        if (placeholderAware && it.isPlaceholder()) {
             SmallImage.PLACEHOLDER
         } else {
             val imageStyle =
@@ -2658,9 +2552,9 @@
         }
     }
 
-private fun WireComplicationData.parseLargeImagePlaceholderAware() =
+private fun WireComplicationData.parseLargeImage(placeholderAware: Boolean = false) =
     largeImage?.let {
-        if (it.isPlaceholder()) {
+        if (placeholderAware && it.isPlaceholder()) {
             PhotoImageComplicationData.PLACEHOLDER
         } else {
             it
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index 60db4fe..de0c253 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -71,10 +71,7 @@
     /** @hide */
     @RestrictTo(RestrictTo.Scope.SUBCLASSES) public fun getTimeDependentText(): TimeDependentText
 
-    /**
-     * Converts this value to [WireComplicationText] object used for serialization.
-     *
-     */
+    /** Converts this value to [WireComplicationText] object used for serialization. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun toWireComplicationText(): WireComplicationText
 
@@ -548,12 +545,10 @@
 }
 
 /** Converts a [WireComplicationText] into an equivalent [ComplicationText] instead. */
-internal fun WireComplicationText.toApiComplicationText(): ComplicationText =
-    DelegatingComplicationText(this)
-
-/** Converts a [WireComplicationText] into an equivalent [ComplicationText] instead. */
-internal fun WireComplicationText.toApiComplicationTextPlaceholderAware(): ComplicationText =
-    if (isPlaceholder) {
+internal fun WireComplicationText.toApiComplicationText(
+    placeholderAware: Boolean = false
+): ComplicationText =
+    if (placeholderAware && isPlaceholder) {
         ComplicationText.PLACEHOLDER
     } else {
         DelegatingComplicationText(this)
@@ -615,8 +610,8 @@
  * A [ComplicationText] where the system evaluates a [DynamicString] on behalf of the watch face. By
  * the time this reaches the watch face's Renderer, it'll have been converted to a plain
  * ComplicationText.
- *
  */
+// TODO(b/269414040): Unhide complication expression APIs.
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class ComplicationTextExpression(public val expression: DynamicString) : ComplicationText {
     private val delegate = DelegatingComplicationText(WireComplicationText(expression))
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
index 3f25f2a..588241b 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
@@ -25,15 +25,20 @@
 import android.support.wearable.complications.ComplicationText.plainText
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
 import androidx.wear.watchface.complications.data.SharedRobolectricTestRunner
+import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert
 import org.junit.Assert.assertThrows
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(SharedRobolectricTestRunner::class)
 public class ComplicationDataTest {
+    @get:Rule val expect = Expect.create()
+
     private val mPendingIntent: PendingIntent? =
         PendingIntent.getBroadcast(
             ApplicationProvider.getApplicationContext(),
@@ -1069,6 +1074,54 @@
         assertThat(entry.placeholder!!.type).isEqualTo(ComplicationData.TYPE_LONG_TEXT)
     }
 
+    enum class HasExpressionWithExpressionScenario(val data: ComplicationData) {
+        RANGED_VALUE(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setRangedValueExpression(DynamicFloat.constant(1f))
+                .build()
+        ),
+        LONG_TEXT(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setLongText(ComplicationText(DynamicString.constant("Long Text")))
+                .build()
+        ),
+        LONG_TITLE(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setLongTitle(ComplicationText(DynamicString.constant("Long Title")))
+                .build()
+        ),
+        SHORT_TEXT(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setShortText(ComplicationText(DynamicString.constant("Short Text")))
+                .build()
+        ),
+        SHORT_TITLE(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setShortTitle(ComplicationText(DynamicString.constant("Short Title")))
+                .build()
+        ),
+        CONTENT_DESCRIPTION(
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+                .setContentDescription(ComplicationText(DynamicString.constant("Description")))
+                .build()
+        ),
+    }
+
+    @Test
+    fun hasExpression_withExpression_returnsTrue() {
+        for (scenario in HasExpressionWithExpressionScenario.values()) {
+            expect.withMessage(scenario.name).that(scenario.data.hasExpression()).isTrue()
+        }
+    }
+
+    @Test
+    fun hasExpression_withoutExpression_returnsFalse() {
+        val data =
+            ComplicationData.Builder(ComplicationData.TYPE_NO_DATA).setRangedValue(10f).build()
+
+        assertThat(data.hasExpression()).isFalse()
+    }
+
     private companion object {
         val TEST_CONTENT_DESCRIPTION: CharSequence = "This is a test description!"
         const val TEST_LONG_TITLE = "what a long title such a long title"
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
index 05f6f9f..c913c25 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
@@ -26,7 +26,6 @@
 import androidx.wear.protolayout.expression.pipeline.StateStore
 import androidx.wear.protolayout.expression.pipeline.TimeGateway
 import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.INVALID_DATA
-import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.hasExpression
 import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
@@ -59,10 +58,14 @@
 
     @Test
     fun evaluate_noExpression_returnsUnevaluated() = runBlocking {
+        val data =
+            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+                .setRangedValue(10f)
+                .build()
+
         val evaluator = ComplicationDataExpressionEvaluator()
 
-        assertThat(evaluator.evaluate(DATA_WITH_NO_EXPRESSION).firstOrNull())
-            .isEqualTo(DATA_WITH_NO_EXPRESSION)
+        assertThat(evaluator.evaluate(data).firstOrNull()).isEqualTo(data)
     }
 
     /**
@@ -203,8 +206,7 @@
         for (scenario in DataWithExpressionScenario.values()) {
             // Defensive copy due to in-place evaluation.
             val expressed = WireComplicationData.Builder(scenario.expressed).build()
-            val stateStore =
-                StateStore(mapOf())
+            val stateStore = StateStore(mapOf())
             val evaluator = ComplicationDataExpressionEvaluator(stateStore)
             val allEvaluations =
                 evaluator
@@ -294,57 +296,7 @@
             )
     }
 
-    enum class HasExpressionDataWithExpressionScenario(val data: WireComplicationData) {
-        RANGED_VALUE(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setRangedValueExpression(DynamicFloat.constant(1f))
-                .build()
-        ),
-        LONG_TEXT(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setLongText(WireComplicationText(DynamicString.constant("Long Text")))
-                .build()
-        ),
-        LONG_TITLE(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setLongTitle(WireComplicationText(DynamicString.constant("Long Title")))
-                .build()
-        ),
-        SHORT_TEXT(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setShortText(WireComplicationText(DynamicString.constant("Short Text")))
-                .build()
-        ),
-        SHORT_TITLE(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setShortTitle(WireComplicationText(DynamicString.constant("Short Title")))
-                .build()
-        ),
-        CONTENT_DESCRIPTION(
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setContentDescription(WireComplicationText(DynamicString.constant("Description")))
-                .build()
-        ),
-    }
-
-    @Test
-    fun hasExpression_dataWithExpression_returnsTrue() {
-        for (scenario in HasExpressionDataWithExpressionScenario.values()) {
-            expect.withMessage(scenario.name).that(hasExpression(scenario.data)).isTrue()
-        }
-    }
-
-    @Test
-    fun hasExpression_dataWithoutExpression_returnsFalse() {
-        assertThat(hasExpression(DATA_WITH_NO_EXPRESSION)).isFalse()
-    }
-
     private companion object {
-        val DATA_WITH_NO_EXPRESSION =
-            WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                .setRangedValue(10f)
-                .build()
-
         /** Converts `[{a: A}, {b: B}, {c: C}]` to `[{a: A}, {a: A, b: B}, {a: A, b: B, c: C}]`. */
         fun <K, V> aggregate(vararg maps: Map<K, V>): List<Map<K, V>> =
             maps.fold(listOf()) { acc, map -> acc + ((acc.lastOrNull() ?: mapOf()) + map) }
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
index f75fff1..356a70f 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
@@ -163,7 +163,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=1, " +
-                    "displayPolicy=1)"
+                    "displayPolicy=1, fallback=null)"
             )
     }
 
@@ -217,7 +217,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -261,7 +261,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -316,7 +316,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -367,7 +367,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -421,7 +421,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
                     "ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -471,7 +471,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -535,7 +535,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -582,7 +582,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -633,7 +633,7 @@
                     "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -683,7 +683,7 @@
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
                     "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
-                    "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+                    "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -746,7 +746,7 @@
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
                     "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
-                    "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+                    "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -801,7 +801,7 @@
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
                     "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
-                    "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+                    "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
     }
 
@@ -874,7 +874,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -943,7 +943,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -983,7 +983,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -1023,7 +1023,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -1094,7 +1094,7 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
     }
 
@@ -1227,7 +1227,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1279,7 +1280,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1340,7 +1342,8 @@
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
                     "persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1401,7 +1404,7 @@
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
                     "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
-                    "interpolated=false), persistencePolicy=0, displayPolicy=0), " +
+                    "interpolated=false), persistencePolicy=0, displayPolicy=0, fallback=null), " +
                     "tapActionLostDueToSerialization=false, " +
                     "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                     "-1000000000-01-01T00:00:00Z, " +
@@ -1466,7 +1469,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1532,7 +1536,8 @@
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
                     "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
                     "interpolated=true), persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1583,7 +1588,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1635,7 +1641,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -1686,7 +1693,8 @@
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                     "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                     "persistencePolicy=0, displayPolicy=0)"
@@ -2856,7 +2864,7 @@
                     "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}, " +
                     "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
         assertThat(data.asWireComplicationData().toString())
             .isEqualTo("ComplicationData{mType=3, mFields=REDACTED}")
@@ -2882,7 +2890,7 @@
                     "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}), " +
                     "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
         assertThat(data.asWireComplicationData().toString())
             .isEqualTo("ComplicationData{mType=4, mFields=REDACTED}")
@@ -2911,7 +2919,7 @@
                     "ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null, " +
                     "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(REDACTED), dataSource=null, colorRamp=null, " +
-                    "persistencePolicy=0, displayPolicy=0)"
+                    "persistencePolicy=0, displayPolicy=0, fallback=null)"
             )
         assertThat(data.asWireComplicationData().toString())
             .isEqualTo("ComplicationData{mType=5, mFields=REDACTED}")
@@ -2940,7 +2948,7 @@
                     "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                     "TimeRange(REDACTED), dataSource=null, colorRamp=ColorRamp(colors=[-65536, " +
                     "-16711936, -16776961], interpolated=true), persistencePolicy=0, " +
-                    "displayPolicy=0)"
+                    "displayPolicy=0, fallback=null)"
             )
         assertThat(data.asWireComplicationData().toString())
             .isEqualTo("ComplicationData{mType=13, mFields=REDACTED}")
@@ -2967,7 +2975,8 @@
                     "mTimeDependentText=null, mExpression=null}), " +
                     "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
-                    "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                    "displayPolicy=0, fallback=null), " +
+                    "tapActionLostDueToSerialization=false, tapAction=null, " +
                     "validTimeRange=TimeRange(REDACTED), persistencePolicy=0, displayPolicy=0)"
             )
         assertThat(data.asWireComplicationData().toString())
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index d777b03e..32cdbdc 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -1835,6 +1835,7 @@
         EditorService.globalEditorService.unregisterObserver(observerId)
     }
 
+    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @Test
     @Suppress("Deprecation") // userStyleSettings
     public fun commit_headless() {
@@ -1886,6 +1887,7 @@
         EditorService.globalEditorService.unregisterObserver(observerId)
     }
 
+    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @SuppressLint("NewApi")
     @Suppress("Deprecation") // userStyleSettings
     @Test
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
index 8c7e17e..eafefe5 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
 import androidx.wear.watchface.control.InteractiveInstanceManager
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.UserStyleSetting
@@ -38,6 +39,7 @@
         InteractiveInstanceManager.setParameterlessEngine(null)
     }
 
+    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @Test
     fun measuresWatchFaceIconsFromCustomContext() {
         val context: Context = ApplicationProvider.getApplicationContext()
diff --git a/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl b/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/wear/wear/src/main/java/androidx/wear/widget/SwipeDismissTransitionHelper.java b/wear/wear/src/main/java/androidx/wear/widget/SwipeDismissTransitionHelper.java
index 7a82c04..3953ac3 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/SwipeDismissTransitionHelper.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/SwipeDismissTransitionHelper.java
@@ -257,10 +257,14 @@
         mCompositingPaint.setColorFilter(null);
         mLayout.setLayerType(View.LAYER_TYPE_NONE, null);
         mLayout.setClipToOutline(false);
-        getOriginalParentView().setBackground(mPrevParentBackground);
+
+        // Restoring previous background
+        ViewGroup originalParentView = getOriginalParentView();
+        if (originalParentView != null) {
+            originalParentView.setBackground(mPrevParentBackground);
+        }
         mPrevParentBackground = null;
     }
-
     private Drawable generateScrimBackgroundDrawable(int width, int height) {
         ShapeDrawable shape = new ShapeDrawable(new RectShape());
         shape.setBounds(0, 0, width, height);
diff --git a/window/extensions/extensions/api/current.ignore b/window/extensions/extensions/api/current.ignore
deleted file mode 100644
index c3f19a2..0000000
--- a/window/extensions/extensions/api/current.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-RemovedDeprecatedMethod: androidx.window.extensions.layout.WindowLayoutComponent#addWindowLayoutInfoListener(android.content.Context, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>):
-    Removed deprecated method androidx.window.extensions.layout.WindowLayoutComponent.addWindowLayoutInfoListener(android.content.Context,java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>)
diff --git a/window/extensions/extensions/api/restricted_current.ignore b/window/extensions/extensions/api/restricted_current.ignore
deleted file mode 100644
index c3f19a2..0000000
--- a/window/extensions/extensions/api/restricted_current.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-RemovedDeprecatedMethod: androidx.window.extensions.layout.WindowLayoutComponent#addWindowLayoutInfoListener(android.content.Context, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>):
-    Removed deprecated method androidx.window.extensions.layout.WindowLayoutComponent.addWindowLayoutInfoListener(android.content.Context,java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>)
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
deleted file mode 100644
index e8fc72a..0000000
--- a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
+++ /dev/null
@@ -1,820 +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.
- */
-
-package androidx.window.embedding
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.graphics.Rect
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.test.core.app.ApplicationProvider
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_ALLOW
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_DISALLOW
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ratio
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LEFT_TO_RIGHT
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.RIGHT_TO_LEFT
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.TOP_TO_BOTTOM
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ADJACENT
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
-import androidx.window.test.R
-import junit.framework.TestCase.assertNull
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertThrows
-import org.junit.Assert.assertTrue
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-
-/**
- * Tests creation of all embedding rule types via builders and from XML.
- * @see SplitPairRule
- * @see SplitRule
- * @see ActivityRule
- */
-class EmbeddingRuleConstructionTests {
-    private val application = ApplicationProvider.getApplicationContext<Context>()
-    private val ruleController = RuleController.getInstance(application)
-    private val density = application.resources.displayMetrics.density
-    private lateinit var validBounds: Rect
-    private lateinit var invalidBounds: Rect
-
-    @Before
-    fun setUp() {
-        validBounds = minValidWindowBounds()
-        invalidBounds = almostValidWindowBounds()
-        ruleController.clearRules()
-    }
-
-    /**
-     * Verifies that default params are set correctly when reading {@link SplitPairRule} from XML.
-     */
-    @Test
-    fun testDefaults_SplitPairRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_default_split_pair_rule)
-        assertEquals(1, rules.size)
-        val rule: SplitPairRule = rules.first() as SplitPairRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
-            .setLayoutDirection(LOCALE)
-            .build()
-        assertNull(rule.tag)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
-        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
-        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
-        assertEquals(false, rule.clearTop)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, validBounds))
-        assertFalse(rule.checkParentBounds(density, invalidBounds))
-    }
-
-    /**
-     * Verifies that params are set correctly when reading {@link SplitPairRule} from XML.
-     * @see R.xml.test_split_config_custom_split_pair_rule for customized value.
-     */
-    @Test
-    fun testCustom_SplitPairRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_custom_split_pair_rule)
-        assertEquals(1, rules.size)
-        val rule: SplitPairRule = rules.first() as SplitPairRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
-            .setLayoutDirection(RIGHT_TO_LEFT)
-            .build()
-        assertEquals("rule2", rule.tag)
-        assertEquals(123, rule.minWidthDp)
-        assertEquals(456, rule.minHeightDp)
-        assertEquals(789, rule.minSmallestWidthDp)
-        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
-        assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
-        assertEquals(ALWAYS, rule.finishPrimaryWithSecondary)
-        assertEquals(NEVER, rule.finishSecondaryWithPrimary)
-        assertEquals(true, rule.clearTop)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-    }
-
-    /** Verifies that horizontal layout are set correctly when reading [SplitPairRule] from XML. */
-    @Test
-    fun testHorizontalLayout_SplitPairRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_split_pair_rule_horizontal_layout)
-        assertEquals(1, rules.size)
-        val rule: SplitPairRule = rules.first() as SplitPairRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
-            .setLayoutDirection(TOP_TO_BOTTOM)
-            .build()
-        assertEquals(TEST_TAG, rule.tag)
-        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
-        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
-        assertEquals(false, rule.clearTop)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, validBounds))
-        assertFalse(rule.checkParentBounds(density, invalidBounds))
-    }
-
-    /**
-     * Verifies that default params are set correctly when creating {@link SplitPairRule} with a
-     * builder.
-     */
-    @Test
-    fun testDefaults_SplitPairRule_Builder() {
-        val rule = SplitPairRule.Builder(HashSet()).build()
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
-            .setLayoutDirection(LOCALE)
-            .build()
-        assertNull(rule.tag)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
-        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
-        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
-        assertEquals(false, rule.clearTop)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, validBounds))
-        assertFalse(rule.checkParentBounds(density, invalidBounds))
-    }
-
-    /**
-     * Verifies that the params are set correctly when creating {@link SplitPairRule} with a
-     * builder.
-     */
-    @Test
-    fun test_SplitPairRule_Builder() {
-        val filters = HashSet<SplitPairFilter>()
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
-            .setLayoutDirection(LEFT_TO_RIGHT)
-            .build()
-        filters.add(
-            SplitPairFilter(
-                ComponentName("a", "b"),
-                ComponentName("c", "d"),
-                "ACTION"
-            )
-        )
-        val rule = SplitPairRule.Builder(filters)
-            .setMinWidthDp(123)
-            .setMinHeightDp(456)
-            .setMinSmallestWidthDp(789)
-            .setMaxAspectRatioInPortrait(ratio(1.23f))
-            .setMaxAspectRatioInLandscape(ratio(4.56f))
-            .setFinishPrimaryWithSecondary(ADJACENT)
-            .setFinishSecondaryWithPrimary(ADJACENT)
-            .setClearTop(true)
-            .setDefaultSplitAttributes(expectedSplitLayout)
-            .setTag(TEST_TAG)
-            .build()
-        assertEquals(ADJACENT, rule.finishPrimaryWithSecondary)
-        assertEquals(ADJACENT, rule.finishSecondaryWithPrimary)
-        assertEquals(true, rule.clearTop)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertEquals(TEST_TAG, rule.tag)
-        assertEquals(filters, rule.filters)
-        assertEquals(123, rule.minWidthDp)
-        assertEquals(456, rule.minHeightDp)
-        assertEquals(789, rule.minSmallestWidthDp)
-        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
-        assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
-    }
-
-    /**
-     * Verifies that illegal parameter values are not allowed when creating {@link SplitPairRule}
-     * with a builder.
-     */
-    @Test
-    fun test_SplitPairRule_Builder_illegalArguments() {
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMinWidthDp(-1)
-                .setMinHeightDp(456)
-                .setMinSmallestWidthDp(789)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMinWidthDp(123)
-                .setMinHeightDp(-1)
-                .setMinSmallestWidthDp(789)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMinWidthDp(123)
-                .setMinHeightDp(456)
-                .setMinSmallestWidthDp(-1)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMaxAspectRatioInPortrait(ratio(-1f))
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMaxAspectRatioInLandscape(ratio(-1f))
-                .build()
-        }
-    }
-
-    /**
-     * Verifies that the SplitPairRule verifies that the parent bounds satisfy
-     * maxAspectRatioInPortrait.
-     */
-    @Test
-    fun testSplitPairRule_maxAspectRatioInPortrait() {
-        // Always allow split
-        var rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .build()
-        var width = 100
-        var height = 1000
-        var bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Always disallow split in portrait
-        rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_DISALLOW)
-            .build()
-        width = 100
-        height = 101
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in landscape
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Compare the aspect ratio in portrait
-        rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ratio(1.1f))
-            .build()
-        // Equals to the max aspect ratio
-        width = 100
-        height = 110
-        bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-        // Greater than the max aspect ratio
-        width = 100
-        height = 111
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in landscape
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-    }
-
-    /**
-     * Verifies that the SplitPairRule verifies that the parent bounds satisfy
-     * maxAspectRatioInLandscape.
-     */
-    @Test
-    fun testSplitPairRule_maxAspectRatioInLandscape() {
-        // Always allow split
-        var rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .build()
-        var width = 1000
-        var height = 100
-        var bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Always disallow split in landscape
-        rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_DISALLOW)
-            .build()
-        width = 101
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in portrait
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Compare the aspect ratio in landscape
-        rule = SplitPairRule.Builder(HashSet())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ratio(1.1f))
-            .build()
-        // Equals to the max aspect ratio
-        width = 110
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-        // Greater than the max aspect ratio
-        width = 111
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in portrait
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-    }
-
-    /**
-     * Verifies that default params are set correctly when reading {@link SplitPlaceholderRule} from
-     * XML.
-     */
-    @Test
-    fun testDefaults_SplitPlaceholderRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_default_split_placeholder_rule)
-        assertEquals(1, rules.size)
-        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
-            .setLayoutDirection(LOCALE)
-            .build()
-        assertNull(rule.tag)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
-        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
-        assertEquals(false, rule.isSticky)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, validBounds))
-        assertFalse(rule.checkParentBounds(density, invalidBounds))
-    }
-
-    /**
-     * Verifies that params are set correctly when reading {@link SplitPlaceholderRule} from XML.
-     * @see R.xml.test_split_config_custom_split_placeholder_rule for customized value.
-     */
-    @Test
-    fun testCustom_SplitPlaceholderRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_custom_split_placeholder_rule)
-        assertEquals(1, rules.size)
-        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
-            .setLayoutDirection(RIGHT_TO_LEFT)
-            .build()
-        assertEquals("rule3", rule.tag)
-        assertEquals(123, rule.minWidthDp)
-        assertEquals(456, rule.minHeightDp)
-        assertEquals(789, rule.minSmallestWidthDp)
-        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
-        assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
-        assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
-        assertEquals(true, rule.isSticky)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-    }
-
-    /**
-     * Verifies that horizontal layout are set correctly when reading [SplitPlaceholderRule]
-     * from XML.
-     */
-    @RequiresApi(Build.VERSION_CODES.M)
-    @Test
-    fun testHorizontalLayout_SplitPlaceholderRule_Xml() {
-        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_split_placeholder_horizontal_layout)
-        assertEquals(1, rules.size)
-        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
-            .setLayoutDirection(BOTTOM_TO_TOP)
-            .build()
-        assertEquals(TEST_TAG, rule.tag)
-        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
-        assertEquals(false, rule.isSticky)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, validBounds))
-        assertFalse(rule.checkParentBounds(density, invalidBounds))
-    }
-
-    /**
-     * Verifies that default params are set correctly when creating {@link SplitPlaceholderRule}
-     * with a builder.
-     */
-    @Test
-    fun testDefaults_SplitPlaceholderRule_Builder() {
-        val rule = SplitPlaceholderRule.Builder(HashSet(), Intent()).build()
-        assertNull(rule.tag)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
-        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
-        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
-        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
-        assertEquals(false, rule.isSticky)
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
-            .setLayoutDirection(LOCALE)
-            .build()
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertTrue(rule.checkParentBounds(density, minValidWindowBounds()))
-        assertFalse(rule.checkParentBounds(density, almostValidWindowBounds()))
-    }
-
-    /**
-     * Verifies that the params are set correctly when creating {@link SplitPlaceholderRule} with a
-     * builder.
-     */
-    @Test
-    fun test_SplitPlaceholderRule_Builder() {
-        val filters = HashSet<ActivityFilter>()
-        filters.add(
-            ActivityFilter(
-                ComponentName("a", "b"),
-                "ACTION"
-            )
-        )
-        val intent = Intent("ACTION")
-        val expectedSplitLayout = SplitAttributes.Builder()
-            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
-            .setLayoutDirection(LEFT_TO_RIGHT)
-            .build()
-        val rule = SplitPlaceholderRule.Builder(filters, intent)
-            .setMinWidthDp(123)
-            .setMinHeightDp(456)
-            .setMinSmallestWidthDp(789)
-            .setMaxAspectRatioInPortrait(ratio(1.23f))
-            .setMaxAspectRatioInLandscape(ratio(4.56f))
-            .setFinishPrimaryWithPlaceholder(ADJACENT)
-            .setSticky(true)
-            .setDefaultSplitAttributes(expectedSplitLayout)
-            .setTag(TEST_TAG)
-            .build()
-        assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
-        assertEquals(true, rule.isSticky)
-        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
-        assertEquals(filters, rule.filters)
-        assertEquals(intent, rule.placeholderIntent)
-        assertEquals(123, rule.minWidthDp)
-        assertEquals(456, rule.minHeightDp)
-        assertEquals(789, rule.minSmallestWidthDp)
-        assertEquals(TEST_TAG, rule.tag)
-        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
-        assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
-    }
-
-    /**
-     * Verifies that illegal parameter values are not allowed when creating
-     * {@link SplitPlaceholderRule} with a builder.
-     */
-    @Test
-    fun test_SplitPlaceholderRule_Builder_illegalArguments() {
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPlaceholderRule.Builder(HashSet(), Intent())
-                .setMinWidthDp(-1)
-                .setMinHeightDp(456)
-                .setMinSmallestWidthDp(789)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPlaceholderRule.Builder(HashSet(), Intent())
-                .setMinWidthDp(123)
-                .setMinHeightDp(-1)
-                .setMinSmallestWidthDp(789)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPlaceholderRule.Builder(HashSet(), Intent())
-                .setMinWidthDp(123)
-                .setMinHeightDp(456)
-                .setMinSmallestWidthDp(-1)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPlaceholderRule.Builder(HashSet(), Intent())
-                .setMinWidthDp(123)
-                .setMinHeightDp(456)
-                .setMinSmallestWidthDp(789)
-                .setFinishPrimaryWithPlaceholder(NEVER)
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMaxAspectRatioInPortrait(ratio(-1f))
-                .build()
-        }
-        assertThrows(IllegalArgumentException::class.java) {
-            SplitPairRule.Builder(HashSet())
-                .setMaxAspectRatioInLandscape(ratio(-1f))
-                .build()
-        }
-    }
-
-    /**
-     * Verifies that the SplitPlaceholderRule verifies that the parent bounds satisfy
-     * maxAspectRatioInPortrait.
-     */
-    @Test
-    fun testSplitPlaceholderRule_maxAspectRatioInPortrait() {
-        // Always allow split
-        var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .build()
-        var width = 100
-        var height = 1000
-        var bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Always disallow split in portrait
-        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_DISALLOW)
-            .build()
-        width = 100
-        height = 101
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in landscape
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Compare the aspect ratio in portrait
-        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ratio(1.1f))
-            .build()
-        // Equals to the max aspect ratio
-        width = 100
-        height = 110
-        bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-        // Greater than the max aspect ratio
-        width = 100
-        height = 111
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in landscape
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-    }
-
-    /**
-     * Verifies that the SplitPlaceholderRule verifies that the parent bounds satisfy
-     * maxAspectRatioInLandscape.
-     */
-    @Test
-    fun testSplitPlaceholderRule_maxAspectRatioInLandscape() {
-        // Always allow split
-        var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
-            .build()
-        var width = 1000
-        var height = 100
-        var bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in portrait
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Always disallow split in landscape
-        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ALWAYS_DISALLOW)
-            .build()
-        width = 101
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in portrait
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-
-        // Compare the aspect ratio in landscape
-        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
-            .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
-            .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
-            .setMaxAspectRatioInLandscape(ratio(1.1f))
-            .build()
-        // Equals to the max aspect ratio
-        width = 110
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertTrue(rule.checkParentBounds(density, bounds))
-        // Greater than the max aspect ratio
-        width = 111
-        height = 100
-        bounds = Rect(0, 0, width, height)
-        assertFalse(rule.checkParentBounds(density, bounds))
-        // Ignore if the bounds in portrait
-        bounds = Rect(0, 0, height, width)
-        assertTrue(rule.checkParentBounds(density, bounds))
-    }
-
-    /**
-     * Verifies that default params are set correctly when reading {@link ActivityRule} from XML.
-     */
-    @Test
-    fun testDefaults_ActivityRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_default_activity_rule)
-        assertEquals(1, rules.size)
-        val rule: ActivityRule = rules.first() as ActivityRule
-        assertNull(rule.tag)
-        assertFalse(rule.alwaysExpand)
-    }
-
-    /**
-     * Verifies that params are set correctly when reading {@link ActivityRule} from XML.
-     * @see R.xml.test_split_config_custom_activity_rule for customized value.
-     */
-    @Test
-    fun testCustom_ActivityRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_custom_activity_rule)
-        assertEquals(1, rules.size)
-        val rule: ActivityRule = rules.first() as ActivityRule
-        assertEquals("rule1", rule.tag)
-        assertTrue(rule.alwaysExpand)
-    }
-
-    /**
-     * Verifies that [ActivityRule.tag] and [ActivityRule.alwaysExpand] are set correctly when
-     * reading [ActivityRule] from XML.
-     */
-    @Test
-    fun testSetTagAndAlwaysExpand_ActivityRule_Xml() {
-        val rules = RuleController
-            .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
-        assertEquals(1, rules.size)
-        val rule: ActivityRule = rules.first() as ActivityRule
-        assertEquals(TEST_TAG, rule.tag)
-        assertTrue(rule.alwaysExpand)
-    }
-
-    /**
-     * Verifies that default params are set correctly when creating {@link ActivityRule} with a
-     * builder.
-     */
-    @Test
-    fun testDefaults_ActivityRule_Builder() {
-        val rule = ActivityRule.Builder(HashSet()).build()
-        assertFalse(rule.alwaysExpand)
-    }
-
-    /**
-     * Verifies that the params are set correctly when creating {@link ActivityRule} with a builder.
-     */
-    @Test
-    fun test_ActivityRule_Builder() {
-        val filters = HashSet<ActivityFilter>()
-        filters.add(
-            ActivityFilter(
-                ComponentName("a", "b"),
-                "ACTION"
-            )
-        )
-        val rule = ActivityRule.Builder(filters)
-            .setAlwaysExpand(true)
-            .setTag(TEST_TAG)
-            .build()
-        assertTrue(rule.alwaysExpand)
-        assertEquals(TEST_TAG, rule.tag)
-        assertEquals(filters, rule.filters)
-    }
-
-    private fun minValidWindowBounds(): Rect {
-        // Get the screen's density scale
-        val scale: Float = density
-        // Convert the dps to pixels, based on density scale
-        val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
-
-        return Rect(0, 0, minValidWidthPx, minValidWidthPx)
-    }
-
-    private fun almostValidWindowBounds(): Rect {
-        // Get the screen's density scale
-        val scale: Float = density
-        // Convert the dps to pixels, based on density scale
-        val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
-
-        return Rect(0, 0, minValidWidthPx, minValidWidthPx)
-    }
-
-    @Test
-    fun testIllegalTag_XML() {
-        assertThrows(IllegalArgumentException::class.java) {
-            RuleController.parseRules(application, R.xml.test_split_config_duplicated_tag)
-        }
-    }
-
-    @Test
-    fun testReplacingRuleWithTag() {
-        var rules = RuleController
-            .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
-        assertEquals(1, rules.size)
-        var rule = rules.first()
-        assertEquals(TEST_TAG, rule.tag)
-        val staticRule = rule as ActivityRule
-        assertTrue(staticRule.alwaysExpand)
-        ruleController.setRules(rules)
-
-        val filters = HashSet<ActivityFilter>()
-        filters.add(
-            ActivityFilter(
-                ComponentName("a", "b"),
-                "ACTION"
-            )
-        )
-        val rule1 = ActivityRule.Builder(filters)
-            .setAlwaysExpand(true)
-            .setTag(TEST_TAG)
-            .build()
-        ruleController.addRule(rule1)
-
-        rules = ruleController.getRules()
-        assertEquals(1, rules.size)
-        rule = rules.first()
-        assertEquals(rule1, rule)
-
-        val intent = Intent("ACTION")
-        val rule2 = SplitPlaceholderRule.Builder(filters, intent)
-            .setMinWidthDp(123)
-            .setMinHeightDp(456)
-            .setMinSmallestWidthDp(789)
-            .setTag(TEST_TAG)
-            .build()
-
-        ruleController.addRule(rule2)
-
-        rules = ruleController.getRules()
-        assertEquals(1, rules.size)
-        rule = rules.first()
-        assertEquals(rule2, rule)
-    }
-
-    companion object {
-        const val TEST_TAG = "test"
-    }
-}
\ No newline at end of file
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
new file mode 100644
index 0000000..2ea8eed
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.test.core.app.ApplicationProvider
+import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_DISALLOW
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.RIGHT_TO_LEFT
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.TOP_TO_BOTTOM
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ADJACENT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import androidx.window.test.R
+import junit.framework.TestCase.assertNull
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests creation of all embedding rule types and from XML.
+ * @see SplitPairRule
+ * @see SplitRule
+ * @see ActivityRule
+ * @see RuleParser
+ */
+class RuleParserTests {
+    private val application = ApplicationProvider.getApplicationContext<Context>()
+    private val ruleController = RuleController.getInstance(application)
+    private val density = application.resources.displayMetrics.density
+    private lateinit var validBounds: Rect
+    private lateinit var invalidBounds: Rect
+
+    @Before
+    fun setUp() {
+        validBounds = minValidWindowBounds()
+        invalidBounds = almostValidWindowBounds()
+        ruleController.clearRules()
+    }
+
+    /**
+     * Verifies that default params are set correctly when reading {@link SplitPairRule} from XML.
+     */
+    @Test
+    fun testDefaults_SplitPairRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_default_split_pair_rule)
+        assertEquals(1, rules.size)
+        val rule: SplitPairRule = rules.first() as SplitPairRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+            .setLayoutDirection(LOCALE)
+            .build()
+        assertNull(rule.tag)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+        assertEquals(false, rule.clearTop)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that params are set correctly when reading {@link SplitPairRule} from XML.
+     * @see R.xml.test_split_config_custom_split_pair_rule for customized value.
+     */
+    @Test
+    fun testCustom_SplitPairRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_custom_split_pair_rule)
+        assertEquals(1, rules.size)
+        val rule: SplitPairRule = rules.first() as SplitPairRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
+            .setLayoutDirection(RIGHT_TO_LEFT)
+            .build()
+        assertEquals("rule2", rule.tag)
+        assertEquals(123, rule.minWidthDp)
+        assertEquals(456, rule.minHeightDp)
+        assertEquals(789, rule.minSmallestWidthDp)
+        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+        assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
+        assertEquals(ALWAYS, rule.finishPrimaryWithSecondary)
+        assertEquals(NEVER, rule.finishSecondaryWithPrimary)
+        assertEquals(true, rule.clearTop)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+    }
+
+    /** Verifies that horizontal layout are set correctly when reading [SplitPairRule] from XML. */
+    @Test
+    fun testHorizontalLayout_SplitPairRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_split_pair_rule_horizontal_layout)
+        assertEquals(1, rules.size)
+        val rule: SplitPairRule = rules.first() as SplitPairRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+            .setLayoutDirection(TOP_TO_BOTTOM)
+            .build()
+        assertEquals(TEST_TAG, rule.tag)
+        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+        assertEquals(false, rule.clearTop)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that default params are set correctly when reading {@link SplitPlaceholderRule} from
+     * XML.
+     */
+    @Test
+    fun testDefaults_SplitPlaceholderRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_default_split_placeholder_rule)
+        assertEquals(1, rules.size)
+        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+            .setLayoutDirection(LOCALE)
+            .build()
+        assertNull(rule.tag)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+        assertEquals(false, rule.isSticky)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that params are set correctly when reading {@link SplitPlaceholderRule} from XML.
+     * @see R.xml.test_split_config_custom_split_placeholder_rule for customized value.
+     */
+    @Test
+    fun testCustom_SplitPlaceholderRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_custom_split_placeholder_rule)
+        assertEquals(1, rules.size)
+        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
+            .setLayoutDirection(RIGHT_TO_LEFT)
+            .build()
+        assertEquals("rule3", rule.tag)
+        assertEquals(123, rule.minWidthDp)
+        assertEquals(456, rule.minHeightDp)
+        assertEquals(789, rule.minSmallestWidthDp)
+        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+        assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
+        assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
+        assertEquals(true, rule.isSticky)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+    }
+
+    /**
+     * Verifies that horizontal layout are set correctly when reading [SplitPlaceholderRule]
+     * from XML.
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
+    @Test
+    fun testHorizontalLayout_SplitPlaceholderRule_Xml() {
+        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_split_placeholder_horizontal_layout)
+        assertEquals(1, rules.size)
+        val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+            .setLayoutDirection(BOTTOM_TO_TOP)
+            .build()
+        assertEquals(TEST_TAG, rule.tag)
+        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+        assertEquals(false, rule.isSticky)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that default params are set correctly when reading {@link ActivityRule} from XML.
+     */
+    @Test
+    fun testDefaults_ActivityRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_default_activity_rule)
+        assertEquals(1, rules.size)
+        val rule: ActivityRule = rules.first() as ActivityRule
+        assertNull(rule.tag)
+        assertFalse(rule.alwaysExpand)
+    }
+
+    /**
+     * Verifies that params are set correctly when reading {@link ActivityRule} from XML.
+     * @see R.xml.test_split_config_custom_activity_rule for customized value.
+     */
+    @Test
+    fun testCustom_ActivityRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_custom_activity_rule)
+        assertEquals(1, rules.size)
+        val rule: ActivityRule = rules.first() as ActivityRule
+        assertEquals("rule1", rule.tag)
+        assertTrue(rule.alwaysExpand)
+    }
+
+    /**
+     * Verifies that [ActivityRule.tag] and [ActivityRule.alwaysExpand] are set correctly when
+     * reading [ActivityRule] from XML.
+     */
+    @Test
+    fun testSetTagAndAlwaysExpand_ActivityRule_Xml() {
+        val rules = RuleController
+            .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
+        assertEquals(1, rules.size)
+        val rule: ActivityRule = rules.first() as ActivityRule
+        assertEquals(TEST_TAG, rule.tag)
+        assertTrue(rule.alwaysExpand)
+    }
+
+    private fun minValidWindowBounds(): Rect {
+        // Get the screen's density scale
+        val scale: Float = density
+        // Convert the dps to pixels, based on density scale
+        val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+
+        return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+    }
+
+    private fun almostValidWindowBounds(): Rect {
+        // Get the screen's density scale
+        val scale: Float = density
+        // Convert the dps to pixels, based on density scale
+        val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+
+        return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+    }
+
+    @Test
+    fun testIllegalTag_XML() {
+        assertThrows(IllegalArgumentException::class.java) {
+            RuleController.parseRules(application, R.xml.test_split_config_duplicated_tag)
+        }
+    }
+
+    @Test
+    fun testReplacingRuleWithTag() {
+        var rules = RuleController
+            .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
+        assertEquals(1, rules.size)
+        var rule = rules.first()
+        assertEquals(TEST_TAG, rule.tag)
+        val staticRule = rule as ActivityRule
+        assertTrue(staticRule.alwaysExpand)
+        ruleController.setRules(rules)
+
+        val filters = HashSet<ActivityFilter>()
+        filters.add(
+            ActivityFilter(
+                ComponentName("a", "b"),
+                "ACTION"
+            )
+        )
+        val rule1 = ActivityRule.Builder(filters)
+            .setAlwaysExpand(true)
+            .setTag(TEST_TAG)
+            .build()
+        ruleController.addRule(rule1)
+
+        rules = ruleController.getRules()
+        assertEquals(1, rules.size)
+        rule = rules.first()
+        assertEquals(rule1, rule)
+
+        val intent = Intent("ACTION")
+        val rule2 = SplitPlaceholderRule.Builder(filters, intent)
+            .setMinWidthDp(123)
+            .setMinHeightDp(456)
+            .setMinSmallestWidthDp(789)
+            .setTag(TEST_TAG)
+            .build()
+
+        ruleController.addRule(rule2)
+
+        rules = ruleController.getRules()
+        assertEquals(1, rules.size)
+        rule = rules.first()
+        assertEquals(rule2, rule)
+    }
+
+    companion object {
+        const val TEST_TAG = "test"
+    }
+}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
index aa266a3..8af21e3 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
@@ -16,12 +16,16 @@
 
 package androidx.window.embedding
 
+import android.content.ComponentName
 import androidx.window.core.ActivityComponentInfo
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
 
+@RunWith(RobolectricTestRunner::class)
 class ActivityRuleTest {
 
     @Test
@@ -60,7 +64,39 @@
         assertEquals(firstRule.hashCode(), secondRule.hashCode())
     }
 
+    /**
+     * Verifies that default params are set correctly when creating [ActivityRule] with a
+     * builder.
+     */
+    @Test
+    fun testDefaults_ActivityRule_Builder() {
+        val rule = ActivityRule.Builder(HashSet()).build()
+        assertFalse(rule.alwaysExpand)
+    }
+
+    /**
+     * Verifies that the params are set correctly when creating [ActivityRule] with a builder.
+     */
+    @Test
+    fun test_ActivityRule_Builder() {
+        val filters = HashSet<ActivityFilter>()
+        filters.add(
+            ActivityFilter(
+                ComponentName("a", "b"),
+                "ACTION"
+            )
+        )
+        val rule = ActivityRule.Builder(filters)
+            .setAlwaysExpand(true)
+            .setTag(TEST_TAG)
+            .build()
+        assertTrue(rule.alwaysExpand)
+        assertEquals(TEST_TAG, rule.tag)
+        assertEquals(filters, rule.filters)
+    }
+
     companion object {
+        private const val TEST_TAG = "test"
         val FILTER_WITH_ACTIVITY = ActivityFilter(
             ActivityComponentInfo("package", "className"),
             null
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
index e8b48a2..d02808d 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
@@ -16,10 +16,27 @@
 
 package androidx.window.embedding
 
+import android.content.ComponentName
+import android.graphics.Rect
 import androidx.window.core.ActivityComponentInfo
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import junit.framework.TestCase
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
 
+/**
+ * Unit test for [SplitPairRule] to check the construction is correct by using it's builder.
+ */
+@RunWith(RobolectricTestRunner::class)
 internal class SplitPairRuleTest {
 
     @Test
@@ -43,4 +60,270 @@
             null
         )
     }
+
+    /*------------------------------Class Test------------------------------*/
+    /**
+     * Test hashcode and equals are properly calculated for 2 equal [SplitPairRule]
+     */
+    @Test
+    fun equalsImpliesHashCode() {
+        val filter = SplitPairFilter(
+            ComponentName("a", "b"),
+            ComponentName("c", "d"),
+            "ACTION"
+        )
+        val firstRule = SplitPairRule.Builder(setOf(filter)).build()
+        val secondRule = SplitPairRule.Builder(setOf(filter)).build()
+        assertEquals(firstRule, secondRule)
+        assertEquals(firstRule.hashCode(), secondRule.hashCode())
+    }
+
+    /*------------------------------Builder Test------------------------------*/
+    /**
+     * Verifies that default params are set correctly when creating [SplitPairRule] with a
+     * builder.
+     */
+    @Test
+    fun testDefaults_SplitPairRule_Builder() {
+        val rule = SplitPairRule.Builder(HashSet()).build()
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+            .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+            .build()
+        TestCase.assertNull(rule.tag)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+        assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+        assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+        assertEquals(false, rule.clearTop)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that the params are set correctly when creating [SplitPairRule] with a
+     * builder.
+     */
+    @Test
+    fun test_SplitPairRule_Builder() {
+        val filters = HashSet<SplitPairFilter>()
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+            .build()
+        filters.add(
+            SplitPairFilter(
+                ComponentName("a", "b"),
+                ComponentName("c", "d"),
+                "ACTION"
+            )
+        )
+        val rule = SplitPairRule.Builder(filters)
+            .setMinWidthDp(123)
+            .setMinHeightDp(456)
+            .setMinSmallestWidthDp(789)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.23f))
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(4.56f))
+            .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.ADJACENT)
+            .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ADJACENT)
+            .setClearTop(true)
+            .setDefaultSplitAttributes(expectedSplitLayout)
+            .setTag(TEST_TAG)
+            .build()
+        assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishPrimaryWithSecondary)
+        assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishSecondaryWithPrimary)
+        assertEquals(true, rule.clearTop)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertEquals(TEST_TAG, rule.tag)
+        assertEquals(filters, rule.filters)
+        assertEquals(123, rule.minWidthDp)
+        assertEquals(456, rule.minHeightDp)
+        assertEquals(789, rule.minSmallestWidthDp)
+        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+        assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
+    }
+
+    /*------------------------------Functional Test------------------------------*/
+    /**
+     * Verifies that illegal parameter values are not allowed when creating [SplitPairRule]
+     * with a builder.
+     */
+    @Test
+    fun test_SplitPairRule_Builder_illegalArguments() {
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMinWidthDp(-1)
+                .setMinHeightDp(456)
+                .setMinSmallestWidthDp(789)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMinWidthDp(123)
+                .setMinHeightDp(-1)
+                .setMinSmallestWidthDp(789)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMinWidthDp(123)
+                .setMinHeightDp(456)
+                .setMinSmallestWidthDp(-1)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(-1f))
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(-1f))
+                .build()
+        }
+    }
+
+    /**
+     * Verifies that the [SplitPairRule] verifies that the parent bounds satisfy
+     * maxAspectRatioInPortrait.
+     */
+    @Test
+    fun testSplitPairRule_maxAspectRatioInPortrait() {
+        // Always allow split
+        var rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .build()
+        var width = 100
+        var height = 1000
+        var bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Always disallow split in portrait
+        rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+            .build()
+        width = 100
+        height = 101
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in landscape
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Compare the aspect ratio in portrait
+        rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.1f))
+            .build()
+        // Equals to the max aspect ratio
+        width = 100
+        height = 110
+        bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+        // Greater than the max aspect ratio
+        width = 100
+        height = 111
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in landscape
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+    }
+
+    /**
+     * Verifies that the [SplitPairRule] verifies that the parent bounds satisfy
+     * maxAspectRatioInLandscape.
+     */
+    @Test
+    fun testSplitPairRule_maxAspectRatioInLandscape() {
+        // Always allow split
+        var rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .build()
+        var width = 1000
+        var height = 100
+        var bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Always disallow split in landscape
+        rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+            .build()
+        width = 101
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in portrait
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Compare the aspect ratio in landscape
+        rule = SplitPairRule.Builder(HashSet())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(1.1f))
+            .build()
+        // Equals to the max aspect ratio
+        width = 110
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+        // Greater than the max aspect ratio
+        width = 111
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in portrait
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+    }
+
+    companion object {
+
+        private const val density = 2f
+        private const val TEST_TAG = "test"
+        private val validBounds: Rect = minValidWindowBounds()
+        private val invalidBounds: Rect = almostValidWindowBounds()
+
+        private fun minValidWindowBounds(): Rect {
+            // Get the screen's density scale
+            val scale: Float = density
+            // Convert the dps to pixels, based on density scale
+            val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+            return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+        }
+
+        private fun almostValidWindowBounds(): Rect {
+            // Get the screen's density scale
+            val scale: Float = density
+            // Convert the dps to pixels, based on density scale
+            val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+            return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+        }
+    }
 }
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
new file mode 100644
index 0000000..0fa21d3
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Rect
+import androidx.window.core.ActivityComponentInfo
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import junit.framework.TestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+/**
+ * Unit test for [SplitPlaceholderRule] to check the construction is correct by using it's builder.
+ */
+@RunWith(RobolectricTestRunner::class)
+internal class SplitPlaceHolderRuleTest {
+
+    /*------------------------------Class Test------------------------------*/
+    /**
+     * Test hashcode and equals are properly calculated for 2 equal [SplitPlaceholderRule]
+     */
+    @Test
+    fun equalsImpliesHashCode() {
+        val filter = ActivityFilter(ActivityComponentInfo("a", "b"), "ACTION")
+        val placeholderIntent = Intent()
+        val firstRule = SplitPlaceholderRule.Builder(setOf(filter), placeholderIntent).build()
+        val secondRule = SplitPlaceholderRule.Builder(setOf(filter), placeholderIntent).build()
+        assertEquals(firstRule, secondRule)
+        assertEquals(firstRule.hashCode(), secondRule.hashCode())
+    }
+
+    /*------------------------------Builder Test------------------------------*/
+    /**
+     * Verifies that default params are set correctly when creating [SplitPlaceholderRule]
+     * with a builder.
+     */
+    @Test
+    fun testDefaults_SplitPlaceholderRule_Builder() {
+        val rule = SplitPlaceholderRule.Builder(HashSet(), Intent()).build()
+        TestCase.assertNull(rule.tag)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+        assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+        assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+        assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+        assertEquals(false, rule.isSticky)
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+            .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+            .build()
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertTrue(rule.checkParentBounds(density, validBounds))
+        assertFalse(rule.checkParentBounds(density, invalidBounds))
+    }
+
+    /**
+     * Verifies that the params are set correctly when creating [SplitPlaceholderRule] with a
+     * builder.
+     */
+    @Test
+    fun test_SplitPlaceholderRule_Builder() {
+        val filters = HashSet<ActivityFilter>()
+        filters.add(
+            ActivityFilter(
+                ComponentName("a", "b"),
+                "ACTION"
+            )
+        )
+        val intent = Intent("ACTION")
+        val expectedSplitLayout = SplitAttributes.Builder()
+            .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+            .build()
+        val rule = SplitPlaceholderRule.Builder(filters, intent)
+            .setMinWidthDp(123)
+            .setMinHeightDp(456)
+            .setMinSmallestWidthDp(789)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.23f))
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(4.56f))
+            .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)
+            .setSticky(true)
+            .setDefaultSplitAttributes(expectedSplitLayout)
+            .setTag(TEST_TAG)
+            .build()
+        assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishPrimaryWithPlaceholder)
+        assertEquals(true, rule.isSticky)
+        assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+        assertEquals(filters, rule.filters)
+        assertEquals(intent, rule.placeholderIntent)
+        assertEquals(123, rule.minWidthDp)
+        assertEquals(456, rule.minHeightDp)
+        assertEquals(789, rule.minSmallestWidthDp)
+        assertEquals(TEST_TAG, rule.tag)
+        assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+        assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
+    }
+
+    /**
+     * Verifies that illegal parameter values are not allowed when creating
+     * [SplitPlaceholderRule] with a builder.
+     */
+    @Test
+    fun test_SplitPlaceholderRule_Builder_illegalArguments() {
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPlaceholderRule.Builder(HashSet(), Intent())
+                .setMinWidthDp(-1)
+                .setMinHeightDp(456)
+                .setMinSmallestWidthDp(789)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPlaceholderRule.Builder(HashSet(), Intent())
+                .setMinWidthDp(123)
+                .setMinHeightDp(-1)
+                .setMinSmallestWidthDp(789)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPlaceholderRule.Builder(HashSet(), Intent())
+                .setMinWidthDp(123)
+                .setMinHeightDp(456)
+                .setMinSmallestWidthDp(-1)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPlaceholderRule.Builder(HashSet(), Intent())
+                .setMinWidthDp(123)
+                .setMinHeightDp(456)
+                .setMinSmallestWidthDp(789)
+                .setFinishPrimaryWithPlaceholder(NEVER)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(-1f))
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPairRule.Builder(HashSet())
+                .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(-1f))
+                .build()
+        }
+    }
+
+    /*------------------------------Functional Test------------------------------*/
+    /**
+     * Verifies that the [SplitPlaceholderRule] verifies that the parent bounds satisfy
+     * maxAspectRatioInPortrait.
+     */
+    @Test
+    fun testSplitPlaceholderRule_maxAspectRatioInPortrait() {
+        // Always allow split
+        var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .build()
+        var width = 100
+        var height = 1000
+        var bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Always disallow split in portrait
+        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+            .build()
+        width = 100
+        height = 101
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in landscape
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Compare the aspect ratio in portrait
+        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.1f))
+            .build()
+        // Equals to the max aspect ratio
+        width = 100
+        height = 110
+        bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+        // Greater than the max aspect ratio
+        width = 100
+        height = 111
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in landscape
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+    }
+
+    /**
+     * Verifies that the [SplitPlaceholderRule] verifies that the parent bounds satisfy
+     * maxAspectRatioInLandscape.
+     */
+    @Test
+    fun testSplitPlaceholderRule_maxAspectRatioInLandscape() {
+        // Always allow split
+        var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .build()
+        var width = 1000
+        var height = 100
+        var bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in portrait
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Always disallow split in landscape
+        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+            .build()
+        width = 101
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in portrait
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+
+        // Compare the aspect ratio in landscape
+        rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+            .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+            .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+            .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(1.1f))
+            .build()
+        // Equals to the max aspect ratio
+        width = 110
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertTrue(rule.checkParentBounds(density, bounds))
+        // Greater than the max aspect ratio
+        width = 111
+        height = 100
+        bounds = Rect(0, 0, width, height)
+        assertFalse(rule.checkParentBounds(density, bounds))
+        // Ignore if the bounds in portrait
+        bounds = Rect(0, 0, height, width)
+        assertTrue(rule.checkParentBounds(density, bounds))
+    }
+
+    companion object {
+
+        private const val density = 2f
+        private const val TEST_TAG = "test"
+        private val validBounds: Rect = minValidWindowBounds()
+        private val invalidBounds: Rect = almostValidWindowBounds()
+
+        private fun minValidWindowBounds(): Rect {
+            // Get the screen's density scale
+            val scale: Float = density
+            // Convert the dps to pixels, based on density scale
+            val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+            return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+        }
+
+        private fun almostValidWindowBounds(): Rect {
+            // Get the screen's density scale
+            val scale: Float = density
+            // Convert the dps to pixels, based on density scale
+            val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+            return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+        }
+    }
+}
\ No newline at end of file
diff --git a/work/work-runtime/api/current.ignore b/work/work-runtime/api/current.ignore
index 7b196e5..ecd7f87 100644
--- a/work/work-runtime/api/current.ignore
+++ b/work/work-runtime/api/current.ignore
@@ -1,14 +1,4 @@
 // Baseline format: 1.0
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfoByIdFlow(java.util.UUID):
-    Added method androidx.work.WorkManager.getWorkInfoByIdFlow(java.util.UUID)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosByTagFlow(String):
-    Added method androidx.work.WorkManager.getWorkInfosByTagFlow(String)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosFlow(androidx.work.WorkQuery):
-    Added method androidx.work.WorkManager.getWorkInfosFlow(androidx.work.WorkQuery)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosForUniqueWorkFlow(String):
-    Added method androidx.work.WorkManager.getWorkInfosForUniqueWorkFlow(String)
-
-
 ChangedType: androidx.work.Configuration#getInitializationExceptionHandler():
     Method androidx.work.Configuration.getInitializationExceptionHandler has changed return type from androidx.core.util.Consumer<java.lang.Throwable!> to androidx.core.util.Consumer<java.lang.Throwable>
 ChangedType: androidx.work.Configuration#getSchedulingExceptionHandler():
diff --git a/work/work-runtime/api/restricted_current.ignore b/work/work-runtime/api/restricted_current.ignore
index 7b196e5..ecd7f87 100644
--- a/work/work-runtime/api/restricted_current.ignore
+++ b/work/work-runtime/api/restricted_current.ignore
@@ -1,14 +1,4 @@
 // Baseline format: 1.0
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfoByIdFlow(java.util.UUID):
-    Added method androidx.work.WorkManager.getWorkInfoByIdFlow(java.util.UUID)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosByTagFlow(String):
-    Added method androidx.work.WorkManager.getWorkInfosByTagFlow(String)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosFlow(androidx.work.WorkQuery):
-    Added method androidx.work.WorkManager.getWorkInfosFlow(androidx.work.WorkQuery)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosForUniqueWorkFlow(String):
-    Added method androidx.work.WorkManager.getWorkInfosForUniqueWorkFlow(String)
-
-
 ChangedType: androidx.work.Configuration#getInitializationExceptionHandler():
     Method androidx.work.Configuration.getInitializationExceptionHandler has changed return type from androidx.core.util.Consumer<java.lang.Throwable!> to androidx.core.util.Consumer<java.lang.Throwable>
 ChangedType: androidx.work.Configuration#getSchedulingExceptionHandler():