Merge "Add a module for Material 3 apdative" into androidx-main
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt
index a091c94..1b0c974 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt
@@ -16,13 +16,14 @@
 
 package androidx.activity
 
+import android.view.View
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.testutils.withActivity
+import androidx.testutils.withUse
 import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Rule
-import androidx.testutils.withUse
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -44,6 +45,21 @@
             }
         }
     }
+
+    @Test
+    fun testReportFullyDrawnRecreate() {
+        val activity = ActivityScenario.launch(ReportFullyDrawnActivity::class.java)
+        activity.withActivity {
+            setContentView(
+                View(this)
+            )
+        }
+        activity.recreate().withActivity {
+            setContentView(
+                View(this)
+            )
+        }
+    }
 }
 
 class ReportFullyDrawnActivity : ComponentActivity()
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index 0028456..db3b9b3 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -172,7 +172,7 @@
                 }
             });
 
-    private final ReportFullyDrawnExecutor mReportFullyDrawnExecutor = createFullyDrawnExecutor();
+    final ReportFullyDrawnExecutor mReportFullyDrawnExecutor = createFullyDrawnExecutor();
 
     @NonNull
     final FullyDrawnReporter mFullyDrawnReporter = new FullyDrawnReporter(
@@ -314,6 +314,7 @@
                     if (!isChangingConfigurations()) {
                         getViewModelStore().clear();
                     }
+                    mReportFullyDrawnExecutor.activityDestroyed();
                 }
             }
         });
@@ -1150,6 +1151,8 @@
 
     private interface ReportFullyDrawnExecutor extends Executor {
         void viewCreated(@NonNull View view);
+
+        void activityDestroyed();
     }
 
     static class ReportFullyDrawnExecutorApi1 implements ReportFullyDrawnExecutor {
@@ -1159,6 +1162,10 @@
         public void viewCreated(@NonNull View view) {
         }
 
+        @Override
+        public void activityDestroyed() {
+        }
+
         /**
          * Called when we want to execute runnable that might call
          * {@link ComponentActivity#reportFullyDrawn()}.
@@ -1191,6 +1198,12 @@
             }
         }
 
+        @Override
+        public void activityDestroyed() {
+            getWindow().getDecorView().removeCallbacks(this);
+            getWindow().getDecorView().getViewTreeObserver().removeOnDrawListener(this);
+        }
+
         /**
          * Called when we want to execute runnable that might call
          * {@link ComponentActivity#reportFullyDrawn()}.
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
index f5cfca8..af8360c 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
@@ -17,8 +17,7 @@
 package androidx.appactions.interaction.capabilities.communication
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
@@ -27,7 +26,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.CALL_TYPE_SPEC
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.PARTICIPANT_TYPE_SPEC
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.Call
 import androidx.appactions.interaction.capabilities.core.values.Call.CallFormat
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
@@ -42,7 +41,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(CreateCall.Property::class.java)
+        .setDescriptor(CreateCall.Properties::class.java)
         .setArguments(CreateCall.Arguments::class.java, CreateCall.Arguments::Builder)
         .setOutput(CreateCall.Output::class.java)
         .bindOptionalParameter(
@@ -74,22 +73,22 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class CreateCall private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
-        >(ACTION_SPEC) {
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+            >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             // TODO(b/268369632): No-op remove empty property builder after Property is removed.
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property
+    class Properties
     internal constructor(
-        val callFormat: ParamProperty<CallFormat>?,
-        val participant: ParamProperty<Participant>?
+        val callFormat: Property<CallFormat>?,
+        val participant: Property<Participant>?
     ) {
         override fun toString(): String {
             return "Property(callFormat=$callFormat, participant=$participant)"
@@ -99,7 +98,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (callFormat != other.callFormat) return false
             if (participant != other.participant) return false
@@ -114,15 +113,15 @@
         }
 
         class Builder {
-            private var callFormat: ParamProperty<CallFormat>? = null
+            private var callFormat: Property<CallFormat>? = null
 
-            private var participant: ParamProperty<Participant>? = null
+            private var participant: Property<Participant>? = null
 
-            fun setCallFormat(callFormat: ParamProperty<CallFormat>): Builder = apply {
+            fun setCallFormat(callFormat: Property<CallFormat>): Builder = apply {
                 this.callFormat = callFormat
             }
 
-            fun build(): Property = Property(callFormat, participant)
+            fun build(): Properties = Properties(callFormat, participant)
         }
     }
 
@@ -237,5 +236,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
index 68e56be..6f428e1 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
@@ -17,8 +17,7 @@
 package androidx.appactions.interaction.capabilities.communication
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
@@ -28,7 +27,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.RECIPIENT_TYPE_SPEC
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.Message
 import androidx.appactions.interaction.capabilities.core.values.properties.Recipient
@@ -42,7 +41,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(CreateMessage.Property::class.java)
+        .setDescriptor(CreateMessage.Properties::class.java)
         .setArguments(CreateMessage.Arguments::class.java, CreateMessage.Arguments::Builder)
         .setOutput(CreateMessage.Output::class.java)
         .bindRepeatedParameter(
@@ -74,22 +73,22 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class CreateMessage private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
-        >(ACTION_SPEC) {
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+            >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             // TODO(b/268369632): No-op remove empty property builder after Property is removed.
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property
+    class Properties
     internal constructor(
-        val recipient: ParamProperty<Recipient>?,
-        val messageText: ParamProperty<StringValue>?
+        val recipient: Property<Recipient>?,
+        val messageText: Property<StringValue>?
     ) {
         override fun toString(): String {
             return "Property(recipient=$recipient, messageText=$messageText)"
@@ -99,7 +98,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (recipient != other.recipient) return false
             if (messageText != other.messageText) return false
@@ -114,18 +113,18 @@
         }
 
         class Builder {
-            private var recipient: ParamProperty<Recipient>? = null
-            private var messageText: ParamProperty<StringValue>? = null
+            private var recipient: Property<Recipient>? = null
+            private var messageText: Property<StringValue>? = null
 
-            fun setRecipient(recipient: ParamProperty<Recipient>): Builder = apply {
+            fun setRecipient(recipient: Property<Recipient>): Builder = apply {
                 this.recipient = recipient
             }
 
-            fun setMessageText(messageText: ParamProperty<StringValue>): Builder = apply {
+            fun setMessageText(messageText: Property<StringValue>): Builder = apply {
                 this.messageText = messageText
             }
 
-            fun build(): Property = Property(recipient, messageText)
+            fun build(): Properties = Properties(recipient, messageText)
         }
     }
 
@@ -238,5 +237,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutor.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutor.kt
index 39fee6a..1a917ef 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutor.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutor.kt
@@ -29,5 +29,5 @@
      * @param arguments the argument for this action.
      * @return the ExecutionResult
      */
-    suspend fun execute(arguments: ArgumentsT): ExecutionResult<OutputT>
+    suspend fun onExecute(arguments: ArgumentsT): ExecutionResult<OutputT>
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutorAsync.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutorAsync.kt
index e049688..7958e11 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutorAsync.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ActionExecutorAsync.kt
@@ -32,18 +32,18 @@
      * @param arguments the argument for this action.
      * @return A ListenableFuture containing the ExecutionResult
      */
-    fun execute(arguments: ArgumentsT): ListenableFuture<ExecutionResult<OutputT>>
+    fun onExecute(arguments: ArgumentsT): ListenableFuture<ExecutionResult<OutputT>>
 
     companion object {
         fun <ArgumentsT, OutputT> ActionExecutor<ArgumentsT, OutputT>.toActionExecutorAsync():
             ActionExecutorAsync<ArgumentsT, OutputT> =
             object : ActionExecutorAsync<ArgumentsT, OutputT> {
                 override val uiHandle = this@toActionExecutorAsync
-                override fun execute(
+                override fun onExecute(
                     arguments: ArgumentsT,
                 ): ListenableFuture<ExecutionResult<OutputT>> =
                     convertToListenableFuture("ActionExecutor#execute") {
-                        [email protected](arguments)
+                        [email protected](arguments)
                     }
             }
     }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseExecutionSession.kt
similarity index 83%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseExecutionSession.kt
index d03b62c..0233d05 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseExecutionSession.kt
@@ -20,8 +20,8 @@
 import androidx.concurrent.futures.await
 import com.google.common.util.concurrent.ListenableFuture
 
-/** Base interface for Session of all verticals. */
-interface BaseSession<ArgumentsT, OutputT> {
+/** Base interface for ExecutionSession of all verticals. */
+interface BaseExecutionSession<ArgumentsT, OutputT> {
     /**
      * Implement any initialization logic.
      *
@@ -35,8 +35,8 @@
      * @param arguments the [ArgumentsT] instance containing data for fulfillment.
      * @return an [ExecutionResult] instance.
      */
-    suspend fun onFinish(arguments: ArgumentsT): ExecutionResult<OutputT> {
-        return onFinishAsync(arguments).await()
+    suspend fun onExecute(arguments: ArgumentsT): ExecutionResult<OutputT> {
+        return onExecuteAsync(arguments).await()
     }
 
     /**
@@ -45,7 +45,7 @@
      * @param arguments the Argument instance containing data for fulfillment.
      * @return a [ListenableFuture] containing an [ExecutionResult] instance.
      */
-    fun onFinishAsync(arguments: ArgumentsT): ListenableFuture<ExecutionResult<OutputT>> {
+    fun onExecuteAsync(arguments: ArgumentsT): ListenableFuture<ExecutionResult<OutputT>> {
         return Futures.immediateFuture(ExecutionResult.Builder<OutputT>().build())
     }
 
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 20243a4..fd3a716 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
@@ -17,28 +17,31 @@
 package androidx.appactions.interaction.capabilities.core
 
 import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.ActionExecutorAsync.Companion.toActionExecutorAsync
 import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
+import androidx.appactions.interaction.capabilities.core.impl.SingleTurnCapabilityImpl
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
+import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
+import androidx.appactions.interaction.capabilities.core.impl.task.TaskCapabilityImpl
+import androidx.appactions.interaction.capabilities.core.impl.task.EmptyTaskUpdater
 import androidx.appactions.interaction.proto.AppActionsContext.AppAction
 
 /**
- * <b>Do not implement this interface yourself.</b>
- *
- * <p>A Capability represents some supported App Action that can be given to App Control.
- *
- * <p>Use helper classes provided by the capability library to get instances of this interface.
+ * A Capability represents some supported Built-In-Intent. Register capabilities within an app to
+ * declare support for the capability.
  */
-interface Capability {
-
+abstract class Capability internal constructor(
     /** Returns the unique Id of this capability declaration. */
-    val id: String
+    open val id: String
+) {
 
     /**
      * Returns an app action proto describing how to fulfill this capability.
      *
      * @suppress
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    fun getAppAction(): AppAction
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    abstract val appAction: AppAction
 
     /**
      * Create a new capability session. The capability library doesn't maintain registry of
@@ -47,8 +50,130 @@
      * @suppress
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    fun createSession(
+    abstract fun createSession(
         sessionId: String,
         hostProperties: HostProperties,
     ): CapabilitySession
+
+    /**
+     * An abstract Builder class for Capability.
+     */
+    abstract class Builder<
+        BuilderT :
+        Builder<
+            BuilderT,
+            PropertyT,
+            ArgumentsT,
+            OutputT,
+            ConfirmationT,
+            ExecutionSessionT,>,
+        PropertyT,
+        ArgumentsT,
+        OutputT,
+        ConfirmationT,
+        ExecutionSessionT : BaseExecutionSession<ArgumentsT, OutputT>,
+        > protected constructor(
+        private val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
+    ) {
+        private var id: String? = null
+        private var property: PropertyT? = null
+        private var actionExecutorAsync: ActionExecutorAsync<ArgumentsT, OutputT>? = null
+        private var sessionFactory: ExecutionSessionFactory<ExecutionSessionT>? = null
+
+        /**
+         * The SessionBridge object, which is used to normalize Session instances to TaskHandler.
+         * see SessionBridge documentation for more information.
+         *
+         * @suppress
+         */
+        @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        protected open val sessionBridge: SessionBridge<ExecutionSessionT, ConfirmationT>? = null
+
+        @Suppress("UNCHECKED_CAST")
+        fun asBuilder(): BuilderT {
+            return this as BuilderT
+        }
+
+        /**
+         * Sets the Id of the capability being built. The Id should be a non-null string that is
+         * unique among all Capability, and should not change during/across activity lifecycles.
+         */
+        fun setId(id: String): BuilderT = asBuilder().apply {
+            this.id = id
+        }
+
+        /**
+         * Sets the Property instance for this capability. Must be called before {@link
+         * Builder#build}.
+         */
+        protected fun setProperty(property: PropertyT) = asBuilder().apply {
+            this.property = property
+        }
+
+        /**
+         * Sets the ActionExecutor for this capability.
+         *
+         * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
+         * nullify the other.
+         *
+         * This method accepts a coroutine-based ActionExecutor instance. There is also an overload
+         * which accepts the ActionExecutorAsync instead.
+         */
+        fun setExecutor(actionExecutor: ActionExecutor<ArgumentsT, OutputT>) = asBuilder().apply {
+            this.actionExecutorAsync = actionExecutor.toActionExecutorAsync()
+        }
+
+        /**
+         * Sets the ActionExecutorAsync for this capability.
+         *
+         * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
+         * nullify the other.
+         *
+         * This method accepts the ActionExecutorAsync interface which returns a ListenableFuture.
+         */
+        fun setExecutor(
+            actionExecutorAsync: ActionExecutorAsync<ArgumentsT, OutputT>,
+        ) = asBuilder().apply {
+            this.actionExecutorAsync = actionExecutorAsync
+        }
+
+        /**
+         * 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.
+         */
+        protected open fun setExecutionSessionFactory(
+            sessionFactory: ExecutionSessionFactory<ExecutionSessionT>,
+        ): BuilderT = asBuilder().apply {
+            this.sessionFactory = sessionFactory
+        }
+
+        /** Builds and returns this Capability. */
+        open fun build(): Capability {
+            val checkedId = requireNotNull(id) { "setId must be called before build" }
+            val checkedProperty = requireNotNull(property) { "property must not be null." }
+            if (actionExecutorAsync != null) {
+                return SingleTurnCapabilityImpl(
+                    checkedId,
+                    actionSpec,
+                    checkedProperty,
+                    actionExecutorAsync!!,
+                )
+            } else {
+                val checkedSessionFactory = requireNotNull(sessionFactory) {
+                    "either setExecutor or setExecutionSessionFactory must be called before build"
+                }
+                return TaskCapabilityImpl(
+                    checkedId,
+                    actionSpec,
+                    checkedProperty,
+                    checkedSessionFactory,
+                    sessionBridge!!,
+                    ::EmptyTaskUpdater,
+                )
+            }
+        }
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityBuilderBase.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityBuilderBase.kt
deleted file mode 100644
index e9a9ce5..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityBuilderBase.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core
-
-import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.ActionExecutorAsync.Companion.toActionExecutorAsync
-import androidx.appactions.interaction.capabilities.core.impl.SingleTurnCapabilityImpl
-import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
-import androidx.appactions.interaction.capabilities.core.impl.task.EmptyTaskUpdater
-import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
-import androidx.appactions.interaction.capabilities.core.impl.task.TaskCapabilityImpl
-
-/**
- * An abstract Builder class for Capability.
- */
-abstract class CapabilityBuilderBase<
-    BuilderT :
-    CapabilityBuilderBase<
-        BuilderT,
-        PropertyT,
-        ArgumentsT,
-        OutputT,
-        ConfirmationT,
-        SessionT,>,
-    PropertyT,
-    ArgumentsT,
-    OutputT,
-    ConfirmationT,
-    SessionT : BaseSession<ArgumentsT, OutputT>,
-    > protected constructor(
-    private val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
-) {
-    private var id: String? = null
-    private var property: PropertyT? = null
-    private var actionExecutorAsync: ActionExecutorAsync<ArgumentsT, OutputT>? = null
-    private var sessionFactory: SessionFactory<SessionT>? = null
-
-    /**
-     * The SessionBridge object, which is used to normalize Session instances to TaskHandler.
-     * see SessionBridge documentation for more information.
-     *
-     * @suppress
-     */
-    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    protected open val sessionBridge: SessionBridge<SessionT, ConfirmationT>? = null
-
-    @Suppress("UNCHECKED_CAST")
-    fun asBuilder(): BuilderT {
-        return this as BuilderT
-    }
-
-    /**
-     * Sets the Id of the capability being built. The Id should be a non-null string that is unique
-     * among all Capability, and should not change during/across activity lifecycles.
-     */
-    fun setId(id: String): BuilderT = asBuilder().apply {
-        this.id = id
-    }
-
-    /**
-     * Sets the Property instance for this capability. Must be called before {@link
-     * CapabilityBuilderBase.build}.
-     */
-    protected fun setProperty(property: PropertyT) = asBuilder().apply {
-        this.property = property
-    }
-
-    /**
-     * Sets the ActionExecutor for this capability.
-     *
-     * setSessionFactory and setExecutor are mutually exclusive, so calling one will nullify
-     * the other.
-     *
-     * This method accepts a coroutine-based ActionExecutor instance. There is also an overload
-     * which accepts the ActionExecutorAsync instead.
-     */
-    fun setExecutor(actionExecutor: ActionExecutor<ArgumentsT, OutputT>) = asBuilder().apply {
-        this.actionExecutorAsync = actionExecutor.toActionExecutorAsync()
-    }
-
-    /**
-     * Sets the ActionExecutorAsync for this capability.
-     *
-     * setSessionFactory and setExecutor are mutually exclusive, so calling one will nullify
-     * the other.
-     *
-     * This method accepts the ActionExecutorAsync interface which returns a ListenableFuture.
-     */
-    fun setExecutor(
-        actionExecutorAsync: ActionExecutorAsync<ArgumentsT, OutputT>,
-    ) = asBuilder().apply {
-        this.actionExecutorAsync = actionExecutorAsync
-    }
-
-    /**
-     * Sets the SessionBuilder instance which is used to create Session instaces for this
-     * capability.
-     *
-     * setSessionFactory and setExecutor are mutually exclusive, so calling one will nullify the other.
-     */
-    protected open fun setSessionFactory(
-        sessionFactory: SessionFactory<SessionT>,
-    ): BuilderT = asBuilder().apply {
-        this.sessionFactory = sessionFactory
-    }
-
-    /** Builds and returns this Capability. */
-    open fun build(): Capability {
-        val checkedId = requireNotNull(id, { "setId must be called before build" })
-        val checkedProperty = requireNotNull(property, { "property must not be null." })
-        if (actionExecutorAsync != null) {
-            return SingleTurnCapabilityImpl(
-                checkedId,
-                actionSpec,
-                checkedProperty,
-                actionExecutorAsync!!,
-            )
-        } else {
-            return TaskCapabilityImpl(
-                checkedId,
-                actionSpec,
-                checkedProperty,
-                requireNotNull(
-                    sessionFactory,
-                    { "either setExecutor or setSessionFactory must be called before build" },
-                ),
-                sessionBridge!!,
-                ::EmptyTaskUpdater,
-            )
-        }
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionFactory.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionSessionFactory.kt
similarity index 82%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionFactory.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionSessionFactory.kt
index 11c32cf..e3e2fb9 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionFactory.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionSessionFactory.kt
@@ -17,18 +17,18 @@
 package androidx.appactions.interaction.capabilities.core
 
 /**
- * Interface to be implemented for creating SessionT instances.
+ * Interface to be implemented for creating [ExecutionSessionT] instances.
  */
-fun interface SessionFactory<SessionT> {
+fun interface ExecutionSessionFactory<ExecutionSessionT> {
     /**
      * Implement this method to create session for handling assistant requests.
      *
      * @param hostProperties only applicable while used with AppInteractionService. Contains the
      *   dimensions of the UI area. Null when used without AppInteractionService.
      *
-     * @return A new SessionT instance for handling a task.
+     * @return A new ExecutionSessionT instance for handling a task.
      */
     fun createSession(
         hostProperties: HostProperties?,
-    ): SessionT
+    ): ExecutionSessionT
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionContext.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionContext.kt
index 5a6246a9..fd9cbca 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionContext.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/SessionContext.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core
 
 /**
- * [SessionContext] contains data passed to {@code BaseSession#onCreate}.
+ * [SessionContext] contains data passed to [BaseExecutionSession.onCreate].
  */
 class SessionContext internal constructor() {
     override fun toString() =
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt
index 8275d0b..c3fcad0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt
@@ -35,14 +35,18 @@
  *
  * <p>Use abstract classes within the library to create instances of the {@link EntityProvider}.
  */
-abstract class EntityProvider<T : Thing> internal constructor(private val typeSpec: TypeSpec<T>) {
+abstract class EntityProvider<T : Thing>
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+constructor(
+    private val typeSpec: TypeSpec<T>
+) {
     private val entityConverter = EntityConverter.of(typeSpec)
 
     /**
      * Unique identifier for this EntityFilter. Must match the shortcuts.xml declaration, which
      * allows different filters to be assigned to types on a per-BII basis.
      */
-    abstract fun getId(): String
+    abstract val id: String
 
     /**
      * Executes the entity lookup.
@@ -116,7 +120,7 @@
     }
 
     private fun convertStatus(
-        @EntityLookupResponse.EntityLookupStatus status: Int,
+        @EntityLookupResponse.EntityLookupStatus status: Int
     ): GroundingResponse.Status {
         return when (status) {
             EntityLookupResponse.CANCELED -> GroundingResponse.Status.CANCELED
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 8875542..6d3b2de 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
@@ -37,7 +37,7 @@
      */
     fun execute(
         argumentsWrapper: ArgumentsWrapper,
-        callback: CallbackInternal,
+        callback: CallbackInternal
     )
 
     /**
@@ -61,7 +61,8 @@
     val status: Status
 
     /**
-     * The developer-provided external object (either a BaseSession instance or an ActionExecutor instance).
+     * The developer-provided external object (either a BaseExecutionSession instance or an
+     * ActionExecutor instance).
      */
     val uiHandle: Any
 
@@ -70,6 +71,6 @@
         UNINITIATED,
         IN_PROGRESS,
         COMPLETED,
-        DESTROYED,
+        DESTROYED
     }
 }
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 bed6582..0fed7e9 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
@@ -32,19 +32,18 @@
     ArgumentsT,
     OutputT,
     > constructor(
-    override val id: String,
+    id: String,
     val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
     val property: PropertyT,
     val actionExecutorAsync: ActionExecutorAsync<ArgumentsT, OutputT>,
-) : Capability {
+) : Capability(id) {
     private val mutex = Mutex()
 
-    override fun getAppAction(): AppAction {
-        return actionSpec.convertPropertyToProto(property).toBuilder()
+    override val appAction: AppAction =
+        actionSpec.convertPropertyToProto(property).toBuilder()
             .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
             .setIdentifier(id)
             .build()
-    }
 
     override fun createSession(
         sessionId: String,
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 0bb3051..40081e8 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
@@ -75,7 +75,7 @@
             try {
                 mutex.lock(owner = this@SingleTurnCapabilitySession)
                 UiHandleRegistry.registerUiHandle(uiHandle, sessionId)
-                val output = actionExecutorAsync.execute(arguments).await()
+                val output = actionExecutorAsync.onExecute(arguments).await()
                 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/spec/ActionSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
index ce31c37..e95f904 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
@@ -24,7 +24,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ParamBinding.ArgumentSetter;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
 import androidx.appactions.interaction.proto.ParamValue;
 
@@ -115,13 +115,13 @@
     }
 
     /**
-     * Binds the parameter name, getter, and setter for a {@link ParamProperty}.
+     * Binds the parameter name, getter, and setter for a {@link Property}.
      *
      * <p>This parameter is required for any capability built from the generated {@link ActionSpec}.
      *
      * @param paramName the name of this action' parameter.
-     * @param propertyGetter a getter of the ParamProperty from the property, which must be able to
-     *     fetch a non-null {@code ParamProperty} from {@code PropertyT}.
+     * @param propertyGetter a getter of the Property from the property, which must be able to
+     *     fetch a non-null {@code Property} from {@code PropertyT}.
      * @param paramConsumer a setter to set the string value in the argument builder.
      * @param paramValueConverter converter FROM assistant ParamValue proto
      * @param entityConverter converter TO assistant Entity proto
@@ -132,7 +132,7 @@
             ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT> bindParameter(
                     @NonNull String paramName,
                     @NonNull
-                            Function<? super PropertyT, ParamProperty<PossibleValueT>>
+                            Function<? super PropertyT, Property<PossibleValueT>>
                                     propertyGetter,
                     @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
                     @NonNull ParamValueConverter<T> paramValueConverter,
@@ -146,15 +146,15 @@
     }
 
     /**
-     * Binds the parameter name, getter, and setter for a {@link ParamProperty}.
+     * Binds the parameter name, getter, and setter for a {@link Property}.
      *
      * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
      * If the Property Optional is not set, this parameter will not exist in the parameter
      * definition of the capability.
      *
      * @param paramName the name of this action' parameter.
-     * @param optionalPropertyGetter an optional getter of the ParamProperty from the property,
-     *     which may be able to fetch a non-null {@code ParamProperty} from {@code PropertyT},
+     * @param optionalPropertyGetter an optional getter of the Property from the property,
+     *     which may be able to fetch a non-null {@code Property} from {@code PropertyT},
      *     or get {@link Optional#empty}.
      * @param paramConsumer a setter to set the string value in the argument builder.
      * @param paramValueConverter converter FROM assistant ParamValue proto
@@ -169,7 +169,7 @@
                             @NonNull
                                     Function<
                                                     ? super PropertyT,
-                                                    Optional<ParamProperty<PossibleValueT>>>
+                                                    Optional<Property<PossibleValueT>>>
                                             optionalPropertyGetter,
                             @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
                             @NonNull ParamValueConverter<T> paramValueConverter,
@@ -206,7 +206,7 @@
                             @NonNull
                                     Function<
                                                     ? super PropertyT,
-                                                    Optional<ParamProperty<PossibleValueT>>>
+                                                    Optional<Property<PossibleValueT>>>
                                             optionalPropertyGetter,
                             @NonNull BiConsumer<? super ArgumentsBuilderT, List<T>> paramConsumer,
                             @NonNull ParamValueConverter<T> paramValueConverter,
@@ -284,11 +284,11 @@
                 mOutputBindings);
     }
 
-    /** Create IntentParameter proto from a ParamProperty. */
+    /** Create IntentParameter proto from a Property. */
     @NonNull
     private static <T> IntentParameter buildIntentParameter(
             @NonNull String paramName,
-            @NonNull ParamProperty<T> property,
+            @NonNull Property<T> property,
             @NonNull EntityConverter<T> entityConverter) {
         IntentParameter.Builder builder = IntentParameter.newBuilder()
                 .setName(paramName)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
index 8c78335..6242b6f8 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
@@ -25,8 +25,8 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 fun interface SessionBridge<
-    SessionT,
+    ExecutionSessionT,
     ConfirmationT
 > {
-    fun createTaskHandler(externalSession: SessionT): TaskHandler<ConfirmationT>
+    fun createTaskHandler(externalSession: ExecutionSessionT): TaskHandler<ConfirmationT>
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
index 09d8f51..fade42a 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
@@ -16,10 +16,10 @@
 
 package androidx.appactions.interaction.capabilities.core.impl.task
 
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
 import androidx.appactions.interaction.capabilities.core.HostProperties
-import androidx.appactions.interaction.capabilities.core.SessionFactory
+import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
 import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.proto.AppActionsContext.AppAction
@@ -29,50 +29,50 @@
 /**
  * @param id a unique id for this capability, can be null
  * @param actionSpec the ActionSpec for this capability
- * @param sessionFactory the SessionFactory provided by the library user
- * @param sessionBridge a SessionBridge object that converts SessionT into TaskHandler instance
+ * @param sessionFactory the ExecutionSessionFactory provided by the library user
+ * @param sessionBridge a SessionBridge object that converts ExecutionSessionT into TaskHandler
+ *           instance
  * @param sessionUpdaterSupplier a Supplier of SessionUpdaterT instances
  */
 internal class TaskCapabilityImpl<
     PropertyT,
     ArgumentsT,
     OutputT,
-    SessionT : BaseSession<ArgumentsT, OutputT>,
+    ExecutionSessionT : BaseExecutionSession<ArgumentsT, OutputT>,
     ConfirmationT,
-    SessionUpdaterT,
->
+    SessionUpdaterT
+    >
 constructor(
-    override val id: String,
+    id: String,
     private val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
     private val property: PropertyT,
-    private val sessionFactory: SessionFactory<SessionT>,
-    private val sessionBridge: SessionBridge<SessionT, ConfirmationT>,
-    private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>,
-) : Capability {
+    private val sessionFactory: ExecutionSessionFactory<ExecutionSessionT>,
+    private val sessionBridge: SessionBridge<ExecutionSessionT, ConfirmationT>,
+    private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>
+) : Capability(id) {
 
-    override fun getAppAction(): AppAction {
-        return actionSpec
+    override val appAction: AppAction =
+        actionSpec
             .convertPropertyToProto(property)
             .toBuilder()
             .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(true))
             .setIdentifier(id)
             .build()
-    }
 
     override fun createSession(
         sessionId: String,
-        hostProperties: HostProperties,
+        hostProperties: HostProperties
     ): CapabilitySession {
         val externalSession =
             sessionFactory.createSession(
-                hostProperties,
+                hostProperties
             )
         return TaskCapabilitySession(
             sessionId,
             actionSpec,
-            getAppAction(),
+            appAction,
             sessionBridge.createTaskHandler(externalSession),
-            externalSession,
+            externalSession
         )
     }
 }
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 baf924b..c4a4b39 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
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl.task
 
 import androidx.annotation.GuardedBy
-import androidx.appactions.interaction.capabilities.core.BaseSession
+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
@@ -41,7 +41,7 @@
     actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
     appAction: AppAction,
     taskHandler: TaskHandler<ConfirmationT>,
-    externalSession: BaseSession<ArgumentsT, OutputT>,
+    externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
     private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
 ) : CapabilitySession, TaskUpdateHandler {
     override val state: AppDialogState
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 ce81aec..30bc097 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
@@ -16,7 +16,7 @@
 package androidx.appactions.interaction.capabilities.core.impl.task
 
 import androidx.annotation.GuardedBy
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.ConfirmationOutput
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.SessionContext
@@ -59,7 +59,7 @@
     private val actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
     private val appAction: AppActionsContext.AppAction,
     private val taskHandler: TaskHandler<ConfirmationT>,
-    private val externalSession: BaseSession<ArgumentsT, OutputT>,
+    private val externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
 ) {
     /**
      * A [reader-writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) to protect
@@ -216,7 +216,7 @@
                     )
                 processFulfillmentValues(fulfillmentValuesMap)
             }
-            val fulfillmentResponse = maybeConfirmOrFinish()
+            val fulfillmentResponse = maybeConfirmOrExecute()
             LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Manual input success")
             if (mTouchEventCallback != null) {
                 mTouchEventCallback!!.onSuccess(
@@ -256,7 +256,7 @@
      * Otherwise, the future contains a FulfillmentResponse containing BIC or BIO data.
      */
     @Throws(StructConversionException::class, MissingRequiredArgException::class)
-    private suspend fun maybeConfirmOrFinish(): FulfillmentResponse {
+    private suspend fun maybeConfirmOrExecute(): FulfillmentResponse {
         val finalArguments = getCurrentAcceptedArguments()
         if (
             anyParamsOfStatus(CurrentValue.Status.REJECTED) ||
@@ -289,7 +289,7 @@
         clearMissingArgs(argumentsWrapper)
         return try {
             processFulfillmentValues(argumentsWrapper.paramValues)
-            val fulfillmentResponse = maybeConfirmOrFinish()
+            val fulfillmentResponse = maybeConfirmOrExecute()
             LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task sync success")
             FulfillmentResult(fulfillmentResponse)
         } catch (t: Throwable) {
@@ -449,7 +449,7 @@
     private suspend fun getFulfillmentResponseForExecution(
         finalArguments: Map<String, List<ParamValue>>,
     ): FulfillmentResponse {
-        val result = externalSession.onFinish(actionSpec.buildArguments(finalArguments))
+        val result = externalSession.onExecute(actionSpec.buildArguments(finalArguments))
         status = CapabilitySession.Status.COMPLETED
         val fulfillmentResponse =
             FulfillmentResponse.newBuilder().setStartDictation(result.shouldStartDictation)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java
index f3240cf..32b3fda 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java
@@ -19,7 +19,7 @@
 import androidx.annotation.NonNull;
 
 /**
- * During the onFinishListener handling, all required params should be present in the Map sent to
+ * During the onExecuteListener handling, all required params should be present in the Map sent to
  * the listener. If they are not for some reason, this is an internal error.
  */
 public final class MissingRequiredArgException extends Exception {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
index 4db410c..b1d84c8 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.properties
 
 /**
- * Entities are used defining possible values for [ParamProperty].
+ * Entities are used defining possible values for [Property].
  */
 class Entity internal constructor(
     val id: String?,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Property.kt
similarity index 86%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Property.kt
index 262fa73..c338ec9 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Property.kt
@@ -20,7 +20,7 @@
  * Configure parameters for the capability such as providing possible values of some type, or
  * marking a parameter as required for execution.
  */
-class ParamProperty<T>
+class Property<T>
 internal constructor(
     private val possibleValueSupplier: () -> List<T>,
     /** Indicates that a value for this property is required to be present for fulfillment. */
@@ -40,18 +40,17 @@
      * assistant.
      */
     @get:JvmName("isProhibited")
-    val isProhibited: Boolean,
+    val isProhibited: Boolean
 ) {
     /** The current list of possible values for this parameter, can change over time. */
     val possibleValues: List<T>
         get() = possibleValueSupplier()
 
-    /** Builder for {@link ParamProperty}. */
+    /** Builder for {@link Property}. */
     class Builder<T> {
-        private var possibleValueSupplier: () -> List<T> = { emptyList<T>() }
+        private var possibleValueSupplier: () -> List<T> = { emptyList() }
         private var isRequired = false
         private var isValueMatchRequired = false
-        private var isProhibited = false
 
         /**
          * Sets one or more possible values for this parameter.
@@ -82,20 +81,25 @@
             this.isValueMatchRequired = valueMatchRequired
         }
 
-        /**
-         * Sets whether this property is prohibited in the response.
-         *
-         * @param prohibited Whether this property is prohibited in the response.
-         */
-        fun setProhibited(prohibited: Boolean) = apply { this.isProhibited = prohibited }
-
         /** Builds the property for this entity parameter. */
         fun build() =
-            ParamProperty(
+            Property(
                 this.possibleValueSupplier,
                 this.isRequired,
                 this.isValueMatchRequired,
-                this.isProhibited,
+                isProhibited = false,
             )
     }
+
+    companion object {
+        @JvmStatic
+        fun <T> prohibited(): Property<T> {
+            return Property(
+                possibleValueSupplier = { emptyList() },
+                isRequired = false,
+                isValueMatchRequired = false,
+                isProhibited = true,
+            )
+        }
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt
index fbe5bef..bddf67d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.properties
 
 /**
- * One of the possible possible values for [ParamProperty].
+ * One of the possible possible values for [Property].
  */
 class StringValue internal constructor(
     val name: String,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/AlarmProvider.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/AlarmProvider.kt
index ff0d2ea..b412f34 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/AlarmProvider.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/AlarmProvider.kt
@@ -21,10 +21,9 @@
 
 /**  Internal testing object for entity provider */
 class AlarmProvider internal constructor(
-    private var id: String,
+    override val id: String,
     private var response: EntityLookupResponse<Alarm>,
 ) : EntityProvider<Alarm>(TypeConverters.ALARM_TYPE_SPEC) {
-    override fun getId(): String = id
     override suspend fun lookup(request: EntityLookupRequest<Alarm>):
         EntityLookupResponse<Alarm> = response
 }
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
index 851f343..de130535 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
@@ -27,15 +27,14 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Entity
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils
 import androidx.appactions.interaction.capabilities.testing.internal.FakeCallbackInternal
 import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.CB_TIMEOUT
 import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.BLOCKING_TIMEOUT
 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.Property
+import androidx.appactions.interaction.capabilities.core.testing.spec.Properties
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue
@@ -70,13 +69,11 @@
                 id = "capabilityId",
                 actionSpec = ACTION_SPEC,
                 property =
-                    Property.newBuilder()
+                Properties.newBuilder()
                         .setRequiredEntityField(
-                            ParamProperty.Builder<Entity>().build(),
+                            Property.Builder<Entity>().build(),
                         )
-                        .setOptionalStringField(
-                            ParamProperty.Builder<StringValue>().setProhibited(true).build(),
-                        )
+                        .setOptionalStringField(Property.prohibited())
                         .build(),
                 actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
             )
@@ -126,13 +123,11 @@
                 id = "capabilityId",
                 actionSpec = ACTION_SPEC,
                 property =
-                    Property.newBuilder()
+                Properties.newBuilder()
                         .setRequiredEntityField(
-                            ParamProperty.Builder<Entity>().build(),
+                            Property.Builder<Entity>().build(),
                         )
-                        .setOptionalStringField(
-                            ParamProperty.Builder<StringValue>().setProhibited(true).build(),
-                        )
+                        .setOptionalStringField(Property.prohibited())
                         .build(),
                 actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
             )
@@ -163,9 +158,9 @@
                 id = "capabilityId",
                 actionSpec = ACTION_SPEC,
                 property =
-                Property.newBuilder()
+                Properties.newBuilder()
                     .setRequiredEntityField(
-                        ParamProperty.Builder<Entity>().build(),
+                        Property.Builder<Entity>().build(),
                     )
                     .build(),
                 actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
@@ -185,9 +180,9 @@
                 id = "capabilityId",
                 actionSpec = ACTION_SPEC,
                 property =
-                Property.newBuilder()
+                Properties.newBuilder()
                     .setRequiredEntityField(
-                        ParamProperty.Builder<Entity>().build(),
+                        Property.Builder<Entity>().build(),
                     )
                     .build(),
                 actionExecutorAsync = actionExecutorAsync,
@@ -209,8 +204,8 @@
         val capability = SingleTurnCapabilityImpl(
             id = "capabilityId",
             actionSpec = ACTION_SPEC,
-            property = Property.newBuilder().setRequiredEntityField(
-                ParamProperty.Builder<Entity>().build(),
+            property = Properties.newBuilder().setRequiredEntityField(
+                Property.Builder<Entity>().build(),
             ).build(),
             actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
         )
@@ -262,16 +257,16 @@
     }
 
     companion object {
-        val ACTION_SPEC: ActionSpec<Property, Arguments, Output> =
+        val ACTION_SPEC: ActionSpec<Properties, Arguments, Output> =
             ActionSpecBuilder.ofCapabilityNamed(
                 "actions.intent.TEST",
             )
-                .setDescriptor(Property::class.java)
+                .setDescriptor(Properties::class.java)
                 .setArguments(Arguments::class.java, Arguments::newBuilder)
                 .setOutput(Output::class.java)
                 .bindOptionalParameter(
                     "optionalString",
-                    Property::optionalStringField,
+                    Properties::optionalStringField,
                     Arguments.Builder::setOptionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
index 261235d..23cf78f 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
@@ -24,7 +24,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.properties.Entity;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.properties.StringValue;
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output;
 import androidx.appactions.interaction.capabilities.core.values.EntityValue;
@@ -46,44 +46,44 @@
 @RunWith(JUnit4.class)
 public final class ActionSpecTest {
 
-    private static final ActionSpec<Property, Arguments, Output> ACTION_SPEC =
+    private static final ActionSpec<Properties, Arguments, Output> ACTION_SPEC =
             ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
-                    .setDescriptor(Property.class)
+                    .setDescriptor(Properties.class)
                     .setArguments(Arguments.class, Arguments::newBuilder)
                     .setOutput(Output.class)
                     .bindParameter(
                             "requiredEntity",
-                            Property::requiredEntityField,
+                            Properties::requiredEntityField,
                             Arguments.Builder::setRequiredEntityField,
                             TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                             TypeConverters.ENTITY_ENTITY_CONVERTER)
                     .bindOptionalParameter(
                             "optionalEntity",
-                            Property::optionalEntityField,
+                            Properties::optionalEntityField,
                             Arguments.Builder::setOptionalEntityField,
                             TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                             TypeConverters.ENTITY_ENTITY_CONVERTER)
                     .bindRepeatedParameter(
                             "repeatedEntity",
-                            Property::repeatedEntityField,
+                            Properties::repeatedEntityField,
                             Arguments.Builder::setRepeatedEntityField,
                             TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                             TypeConverters.ENTITY_ENTITY_CONVERTER)
                     .bindParameter(
                             "requiredString",
-                            Property::requiredStringField,
+                            Properties::requiredStringField,
                             Arguments.Builder::setRequiredStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
                     .bindOptionalParameter(
                             "optionalString",
-                            Property::optionalStringField,
+                            Properties::optionalStringField,
                             Arguments.Builder::setOptionalStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
                     .bindRepeatedParameter(
                             "repeatedString",
-                            Property::repeatedStringField,
+                            Properties::repeatedStringField,
                             Arguments.Builder::setRepeatedStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
@@ -145,17 +145,17 @@
     public void getAppAction_genericParameters() {
         GenericEntityProperty property =
                 GenericEntityProperty.create(
-                        new ParamProperty.Builder<String>()
+                        new Property.Builder<String>()
                                 .setRequired(true)
                                 .setPossibleValues("one")
                                 .build(),
                         Optional.of(
-                                new ParamProperty.Builder<String>()
+                                new Property.Builder<String>()
                                         .setRequired(true)
                                         .setPossibleValues("two")
                                         .build()),
                         Optional.of(
-                                new ParamProperty.Builder<String>()
+                                new Property.Builder<String>()
                                         .setRequired(true)
                                         .setPossibleValues("three")
                                         .build()));
@@ -196,9 +196,9 @@
 
     @Test
     public void getAppAction_onlyRequiredProperty() {
-        Property property =
-                Property.create(
-                        new ParamProperty.Builder<Entity>()
+        Properties property =
+                Properties.create(
+                        new Property.Builder<Entity>()
                                 .setPossibleValues(
                                         new Entity.Builder()
                                                 .setId("contact_2")
@@ -207,7 +207,7 @@
                                                 .build())
                                 .setValueMatchRequired(true)
                                 .build(),
-                        new ParamProperty.Builder<StringValue>().build());
+                        new Property.Builder<StringValue>().build());
 
         assertThat(ACTION_SPEC.convertPropertyToProto(property))
                 .isEqualTo(
@@ -231,9 +231,9 @@
 
     @Test
     public void getAppAction_allProperties() {
-        Property property =
-                Property.create(
-                        new ParamProperty.Builder<Entity>()
+        Properties property =
+                Properties.create(
+                        new Property.Builder<Entity>()
                                 .setPossibleValues(
                                         new Entity.Builder()
                                                 .setId("contact_2")
@@ -242,7 +242,7 @@
                                                 .build())
                                 .build(),
                         Optional.of(
-                                new ParamProperty.Builder<Entity>()
+                                new Property.Builder<Entity>()
                                         .setPossibleValues(
                                                 new Entity.Builder()
                                                         .setId("entity1")
@@ -251,12 +251,12 @@
                                         .setRequired(true)
                                         .build()),
                         Optional.of(
-                                new ParamProperty.Builder<TestEnum>()
+                                new Property.Builder<TestEnum>()
                                         .setPossibleValues(TestEnum.VALUE_1)
                                         .setRequired(true)
                                         .build()),
                         Optional.of(
-                                new ParamProperty.Builder<Entity>()
+                                new Property.Builder<Entity>()
                                         .setPossibleValues(
                                                 new Entity.Builder()
                                                         .setId("entity1")
@@ -268,17 +268,14 @@
                                                         .build())
                                         .setRequired(true)
                                         .build()),
-                        new ParamProperty.Builder<StringValue>().build(),
+                        new Property.Builder<StringValue>().build(),
                         Optional.of(
-                                new ParamProperty.Builder<StringValue>()
+                                new Property.Builder<StringValue>()
                                         .setPossibleValues(StringValue.of("value1"))
                                         .setValueMatchRequired(true)
                                         .setRequired(true)
                                         .build()),
-                        Optional.of(
-                                new ParamProperty.Builder<StringValue>()
-                                        .setProhibited(true)
-                                        .build()));
+                        Optional.of(Property.prohibited()));
 
         assertThat(ACTION_SPEC.convertPropertyToProto(property))
                 .isEqualTo(
@@ -453,17 +450,17 @@
     }
 
     @AutoValue
-    abstract static class Property {
+    abstract static class Properties {
 
-        static Property create(
-                ParamProperty<Entity> requiredEntityField,
-                Optional<ParamProperty<Entity>> optionalEntityField,
-                Optional<ParamProperty<TestEnum>> optionalEnumField,
-                Optional<ParamProperty<Entity>> repeatedEntityField,
-                ParamProperty<StringValue> requiredStringField,
-                Optional<ParamProperty<StringValue>> optionalStringField,
-                Optional<ParamProperty<StringValue>> repeatedStringField) {
-            return new AutoValue_ActionSpecTest_Property(
+        static Properties create(
+                Property<Entity> requiredEntityField,
+                Optional<Property<Entity>> optionalEntityField,
+                Optional<Property<TestEnum>> optionalEnumField,
+                Optional<Property<Entity>> repeatedEntityField,
+                Property<StringValue> requiredStringField,
+                Optional<Property<StringValue>> optionalStringField,
+                Optional<Property<StringValue>> repeatedStringField) {
+            return new AutoValue_ActionSpecTest_Properties(
                     requiredEntityField,
                     optionalEntityField,
                     optionalEnumField,
@@ -473,9 +470,9 @@
                     repeatedStringField);
         }
 
-        static Property create(
-                ParamProperty<Entity> requiredEntityField,
-                ParamProperty<StringValue> requiredStringField) {
+        static Properties create(
+                Property<Entity> requiredEntityField,
+                Property<StringValue> requiredStringField) {
             return create(
                     requiredEntityField,
                     Optional.empty(),
@@ -486,19 +483,19 @@
                     Optional.empty());
         }
 
-        abstract ParamProperty<Entity> requiredEntityField();
+        abstract Property<Entity> requiredEntityField();
 
-        abstract Optional<ParamProperty<Entity>> optionalEntityField();
+        abstract Optional<Property<Entity>> optionalEntityField();
 
-        abstract Optional<ParamProperty<TestEnum>> optionalEnumField();
+        abstract Optional<Property<TestEnum>> optionalEnumField();
 
-        abstract Optional<ParamProperty<Entity>> repeatedEntityField();
+        abstract Optional<Property<Entity>> repeatedEntityField();
 
-        abstract ParamProperty<StringValue> requiredStringField();
+        abstract Property<StringValue> requiredStringField();
 
-        abstract Optional<ParamProperty<StringValue>> optionalStringField();
+        abstract Optional<Property<StringValue>> optionalStringField();
 
-        abstract Optional<ParamProperty<StringValue>> repeatedStringField();
+        abstract Optional<Property<StringValue>> repeatedStringField();
     }
 
     @AutoValue
@@ -533,17 +530,17 @@
     abstract static class GenericEntityProperty {
 
         static GenericEntityProperty create(
-                ParamProperty<String> singularField,
-                Optional<ParamProperty<String>> optionalField,
-                Optional<ParamProperty<String>> repeatedField) {
+                Property<String> singularField,
+                Optional<Property<String>> optionalField,
+                Optional<Property<String>> repeatedField) {
             return new AutoValue_ActionSpecTest_GenericEntityProperty(
                     singularField, optionalField, repeatedField);
         }
 
-        abstract ParamProperty<String> singularField();
+        abstract Property<String> singularField();
 
-        abstract Optional<ParamProperty<String>> optionalField();
+        abstract Optional<Property<String>> optionalField();
 
-        abstract Optional<ParamProperty<String>> repeatedField();
+        abstract Optional<Property<String>> repeatedField();
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/ParamPropertyTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/PropertyTest.kt
similarity index 93%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/ParamPropertyTest.kt
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/PropertyTest.kt
index 8ca80c8..7563a3f 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/ParamPropertyTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/PropertyTest.kt
@@ -22,11 +22,11 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class ParamPropertyTest {
+class PropertyTest {
     @Test
     fun dynamicInventory_test() {
         val mutablePossibleValues = mutableListOf<String>("a", "b")
-        val testProperty = ParamProperty.Builder<String>()
+        val testProperty = Property.Builder<String>()
             .setPossibleValueSupplier { mutablePossibleValues.toList() }
             .build()
 
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 9100ff9..d403689 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
@@ -18,12 +18,11 @@
 import android.util.SizeF
 import androidx.appactions.interaction.capabilities.core.AppEntityListener
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.EntitySearchResult
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
+import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.SessionContext
-import androidx.appactions.interaction.capabilities.core.SessionFactory
 import androidx.appactions.interaction.capabilities.core.ValidationResult
 import androidx.appactions.interaction.capabilities.core.ValueListener
 import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
@@ -38,15 +37,15 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.testing.spec.Arguments
 import androidx.appactions.interaction.capabilities.core.testing.spec.CapabilityStructFill
 import androidx.appactions.interaction.capabilities.core.testing.spec.CapabilityTwoEntityValues
 import androidx.appactions.interaction.capabilities.core.testing.spec.Confirmation
+import androidx.appactions.interaction.capabilities.core.testing.spec.ExecutionSession
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output
-import androidx.appactions.interaction.capabilities.core.testing.spec.Property
-import androidx.appactions.interaction.capabilities.core.testing.spec.Session
 import androidx.appactions.interaction.capabilities.core.testing.spec.TestEnum
+import androidx.appactions.interaction.capabilities.core.testing.spec.Properties
 import androidx.appactions.interaction.capabilities.core.values.EntityValue
 import androidx.appactions.interaction.capabilities.core.values.ListItem
 import androidx.appactions.interaction.capabilities.core.values.SearchAction
@@ -88,8 +87,8 @@
             SINGLE_REQUIRED_FIELD_PROPERTY,
             sessionFactory =
             {
-                object : Session {
-                    override fun onFinishAsync(arguments: Arguments) =
+                object : ExecutionSession {
+                    override fun onExecuteAsync(arguments: Arguments) =
                         Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
                 }
             },
@@ -106,7 +105,7 @@
 
     @Test
     fun getAppAction_smokeTest() {
-        assertThat(capability.getAppAction())
+        assertThat(capability.appAction)
             .isEqualTo(
                 AppAction.newBuilder()
                     .setName("actions.intent.TEST")
@@ -123,7 +122,7 @@
 
     @Test
     fun capabilitySession_getUiHandle() {
-        val externalSession = object : Session {}
+        val externalSession = object : ExecutionSession {}
         val capability =
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
@@ -143,12 +142,12 @@
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
                 sessionFactory =
-                SessionFactory {
-                    object : Session {
+                ExecutionSessionFactory {
+                    object : ExecutionSession {
                         override fun onCreate(sessionContext: SessionContext) {
                             onCreateInvocationCount.incrementAndGet()
                         }
-                        override fun onFinishAsync(arguments: Arguments) =
+                        override fun onExecuteAsync(arguments: Arguments) =
                             Futures.immediateFuture(
                                 ExecutionResult.Builder<Output>().build(),
                             )
@@ -220,17 +219,17 @@
 
     @Test
     fun duringExecution_uiHandleRegistered(): Unit = runBlocking {
-        val onFinishReached = CompletableDeferred<Unit>()
-        val onFinishResult = CompletableDeferred<ExecutionResult<Output>>()
-        val externalSession = object : Session {
-            override suspend fun onFinish(arguments: Arguments): ExecutionResult<Output> {
-                onFinishReached.complete(Unit)
-                return onFinishResult.await()
+        val onExecuteReached = CompletableDeferred<Unit>()
+        val onExecuteResult = CompletableDeferred<ExecutionResult<Output>>()
+        val externalSession = object : ExecutionSession {
+            override suspend fun onExecute(arguments: Arguments): ExecutionResult<Output> {
+                onExecuteReached.complete(Unit)
+                return onExecuteResult.await()
             }
         }
         val capability: Capability = createCapability(
             SINGLE_REQUIRED_FIELD_PROPERTY,
-            sessionFactory = SessionFactory { externalSession },
+            sessionFactory = ExecutionSessionFactory { externalSession },
             sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
             sessionUpdaterSupplier = ::RequiredTaskUpdater,
         )
@@ -244,12 +243,12 @@
             ),
             callback,
         )
-        onFinishReached.await()
+        onExecuteReached.await()
         assertThat(UiHandleRegistry.getSessionIdFromUiHandle(externalSession)).isEqualTo(
             "mySessionId",
         )
 
-        onFinishResult.complete(ExecutionResult.Builder<Output>().build())
+        onExecuteResult.complete(ExecutionResult.Builder<Output>().build())
         assertThat(callback.receiveResponse().fulfillmentResponse).isNotNull()
         assertThat(UiHandleRegistry.getSessionIdFromUiHandle(externalSession)).isNull()
     }
@@ -261,9 +260,9 @@
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
                 sessionFactory =
-                SessionFactory {
-                    object : Session {
-                        override fun onFinishAsync(arguments: Arguments) =
+                ExecutionSessionFactory {
+                    object : ExecutionSession {
+                        override fun onExecuteAsync(arguments: Arguments) =
                             Futures.immediateFuture(
                                 ExecutionResult.Builder<Output>().build(),
                             )
@@ -274,7 +273,7 @@
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
 
-        assertThat(capability.getAppAction())
+        assertThat(capability.appAction)
             .isEqualTo(
                 AppAction.newBuilder()
                     .setName("actions.intent.TEST")
@@ -297,17 +296,17 @@
 
     @Test
     fun slotFilling_getStatus_smokeTest() {
-        val property: CapabilityTwoEntityValues.Property =
-            CapabilityTwoEntityValues.Property.newBuilder()
+        val property: CapabilityTwoEntityValues.Properties =
+            CapabilityTwoEntityValues.Properties.newBuilder()
                 .setSlotA(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(true)
                         .build(),
                 )
                 .setSlotB(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(true)
@@ -315,15 +314,15 @@
                 )
                 .build()
         val sessionFactory =
-            SessionFactory<CapabilityTwoEntityValues.Session> {
-                object : CapabilityTwoEntityValues.Session {
-                    override suspend fun onFinish(
+            ExecutionSessionFactory<CapabilityTwoEntityValues.ExecutionSession> {
+                object : CapabilityTwoEntityValues.ExecutionSession {
+                    override suspend fun onExecute(
                         arguments: CapabilityTwoEntityValues.Arguments,
                     ): ExecutionResult<Void> = ExecutionResult.Builder<Void>().build()
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityTwoEntityValues.Session, Void> {
+            SessionBridge<CapabilityTwoEntityValues.ExecutionSession, Void> {
                 TaskHandler.Builder<Void>()
                     .registerValueTaskParam(
                         "slotA",
@@ -391,18 +390,18 @@
     @Test
     @kotlin.Throws(Exception::class)
     fun slotFilling_optionalButRejectedParam_onFinishNotInvoked() {
-        val onFinishInvocationCount = AtomicInteger(0)
-        val property: CapabilityTwoEntityValues.Property =
-            CapabilityTwoEntityValues.Property.newBuilder()
+        val onExecuteInvocationCount = AtomicInteger(0)
+        val property: CapabilityTwoEntityValues.Properties =
+            CapabilityTwoEntityValues.Properties.newBuilder()
                 .setSlotA(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(true)
                         .build(),
                 )
                 .setSlotB(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(false)
@@ -410,18 +409,18 @@
                 )
                 .build()
         val sessionFactory =
-            SessionFactory<CapabilityTwoEntityValues.Session> {
-                object : CapabilityTwoEntityValues.Session {
-                    override suspend fun onFinish(
+            ExecutionSessionFactory<CapabilityTwoEntityValues.ExecutionSession> {
+                object : CapabilityTwoEntityValues.ExecutionSession {
+                    override suspend fun onExecute(
                         arguments: CapabilityTwoEntityValues.Arguments,
                     ): ExecutionResult<Void> {
-                        onFinishInvocationCount.incrementAndGet()
+                        onExecuteInvocationCount.incrementAndGet()
                         return ExecutionResult.Builder<Void>().build()
                     }
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityTwoEntityValues.Session, Void> {
+            SessionBridge<CapabilityTwoEntityValues.ExecutionSession, Void> {
                 TaskHandler.Builder<Void>()
                     .registerValueTaskParam(
                         "slotA",
@@ -459,7 +458,7 @@
             callback,
         )
         assertThat(callback.receiveResponse().fulfillmentResponse).isNotNull()
-        assertThat(onFinishInvocationCount.get()).isEqualTo(0)
+        assertThat(onExecuteInvocationCount.get()).isEqualTo(0)
         assertThat(getCurrentValues("slotA", session.state))
             .containsExactly(
                 CurrentValue.newBuilder()
@@ -483,17 +482,17 @@
     @Test
     @kotlin.Throws(Exception::class)
     fun slotFilling_assistantRemovedParam_clearInSdkState() {
-        val property: Property =
-            Property.newBuilder()
+        val property: Properties =
+            Properties.newBuilder()
                 .setRequiredEntityField(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(true)
                         .build(),
                 )
                 .setEnumField(
-                    ParamProperty.Builder<TestEnum>()
+                    Property.Builder<TestEnum>()
                         .setPossibleValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
                         .setRequired(true)
                         .build(),
@@ -502,7 +501,7 @@
         val capability: Capability =
             createCapability(
                 property,
-                sessionFactory = SessionFactory { Session.DEFAULT },
+                sessionFactory = ExecutionSessionFactory { ExecutionSession.DEFAULT },
                 sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
                 sessionUpdaterSupplier = ::EmptyTaskUpdater,
             )
@@ -561,8 +560,8 @@
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
                 sessionFactory = {
-                    object : Session {
-                        override suspend fun onFinish(arguments: Arguments) =
+                    object : ExecutionSession {
+                        override suspend fun onExecute(arguments: Arguments) =
                             ExecutionResult.Builder<Output>().build()
 
                         override fun getRequiredEntityListener() =
@@ -588,7 +587,7 @@
                     }
                 },
                 sessionBridge =
-                SessionBridge<Session, Confirmation> { session ->
+                SessionBridge<ExecutionSession, Confirmation> { session ->
                     val builder = TaskHandler.Builder<Confirmation>()
                     session.getRequiredEntityListener()
                         ?.let { listener: AppEntityListener<EntityValue> ->
@@ -699,27 +698,27 @@
     @kotlin.Throws(Exception::class)
     @Suppress("DEPRECATION") // TODO(b/269638788) migrate session state to AppDialogState message
     fun identifierOnly_refillsStruct() = runBlocking<Unit> {
-        val property: CapabilityStructFill.Property =
-            CapabilityStructFill.Property.newBuilder()
-                .setListItem(ParamProperty.Builder<ListItem>().setRequired(true).build())
-                .setAnyString(ParamProperty.Builder<StringValue>().setRequired(true).build())
+        val property: CapabilityStructFill.Properties =
+            CapabilityStructFill.Properties.newBuilder()
+                .setListItem(Property.Builder<ListItem>().setRequired(true).build())
+                .setAnyString(Property.Builder<StringValue>().setRequired(true).build())
                 .build()
         val item1: ListItem = ListItem.newBuilder().setName("red apple").setId("item1").build()
         val item2: ListItem = ListItem.newBuilder().setName("green apple").setId("item2").build()
         val onReceivedDeferred = CompletableDeferred<ListItem>()
-        val onFinishListItemDeferred = CompletableDeferred<ListItem>()
-        val onFinishStringDeferred = CompletableDeferred<String>()
+        val onExecuteListItemDeferred = CompletableDeferred<ListItem>()
+        val onExecuteStringDeferred = CompletableDeferred<String>()
 
         val sessionFactory =
-            SessionFactory<CapabilityStructFill.Session> {
-                object : CapabilityStructFill.Session {
-                    override suspend fun onFinish(
+            ExecutionSessionFactory<CapabilityStructFill.ExecutionSession> {
+                object : CapabilityStructFill.ExecutionSession {
+                    override suspend fun onExecute(
                         arguments: CapabilityStructFill.Arguments,
                     ): ExecutionResult<Void> {
                         val listItem: ListItem = arguments.listItem().orElse(null)
                         val string: String = arguments.anyString().orElse(null)
-                        onFinishListItemDeferred.complete(listItem)
-                        onFinishStringDeferred.complete(string)
+                        onExecuteListItemDeferred.complete(listItem)
+                        onExecuteStringDeferred.complete(string)
                         return ExecutionResult.Builder<Void>().build()
                     }
 
@@ -745,7 +744,7 @@
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityStructFill.Session, Void> { session ->
+            SessionBridge<CapabilityStructFill.ExecutionSession, Void> { session ->
                 TaskHandler.Builder<Void>()
                     .registerAppEntityTaskParam(
                         "listItem",
@@ -776,7 +775,7 @@
         )
         assertThat(callback.receiveResponse().fulfillmentResponse).isNotNull()
         assertThat(onReceivedDeferred.isCompleted).isFalse()
-        assertThat(onFinishListItemDeferred.isCompleted).isFalse()
+        assertThat(onExecuteListItemDeferred.isCompleted).isFalse()
         assertThat(session.state)
             .isEqualTo(
                 AppDialogState.newBuilder()
@@ -824,7 +823,7 @@
         )
         assertThat(callback2.receiveResponse().fulfillmentResponse).isNotNull()
         assertThat(onReceivedDeferred.awaitSync()).isEqualTo(item2)
-        assertThat(onFinishListItemDeferred.isCompleted).isFalse()
+        assertThat(onExecuteListItemDeferred.isCompleted).isFalse()
 
         // third sync request, sending grounded ParamValue with identifier only, completes task
         val callback3 = FakeCallbackInternal()
@@ -839,17 +838,17 @@
             callback3,
         )
         assertThat(callback3.receiveResponse().fulfillmentResponse).isNotNull()
-        assertThat(onFinishListItemDeferred.awaitSync()).isEqualTo(item2)
-        assertThat(onFinishStringDeferred.awaitSync()).isEqualTo("unused")
+        assertThat(onExecuteListItemDeferred.awaitSync()).isEqualTo(item2)
+        assertThat(onExecuteStringDeferred.awaitSync()).isEqualTo("unused")
     }
 
     @Test
     @kotlin.Throws(Exception::class)
     fun executionResult_resultReturned() {
         val sessionFactory =
-            SessionFactory<Session> {
-                object : Session {
-                    override suspend fun onFinish(arguments: Arguments) =
+            ExecutionSessionFactory<ExecutionSession> {
+                object : ExecutionSession {
+                    override suspend fun onExecute(arguments: Arguments) =
                         ExecutionResult.Builder<Output>()
                             .setOutput(
                                 Output.builder()
@@ -863,7 +862,7 @@
                 }
             }
         val capability =
-            CapabilityBuilder().setId("fakeId").setSessionFactory(sessionFactory).build()
+            CapabilityBuilder().setId("fakeId").setExecutionSessionFactory(sessionFactory).build()
         val session = capability.createSession(fakeSessionId, hostProperties)
         val callback = FakeCallbackInternal()
         val expectedOutput: StructuredOutput =
@@ -909,16 +908,16 @@
     @kotlin.Throws(Exception::class)
     fun executionResult_shouldStartDictation_resultReturned() {
         val sessionFactory =
-            SessionFactory<Session> {
-                object : Session {
-                    override suspend fun onFinish(arguments: Arguments) =
+            ExecutionSessionFactory<ExecutionSession> {
+                object : ExecutionSession {
+                    override suspend fun onExecute(arguments: Arguments) =
                         ExecutionResult.Builder<Output>()
                             .setStartDictation(true)
                             .build()
                 }
             }
         val capability =
-            CapabilityBuilder().setId("fakeId").setSessionFactory(sessionFactory).build()
+            CapabilityBuilder().setId("fakeId").setExecutionSessionFactory(sessionFactory).build()
         val session = capability.createSession(fakeSessionId, hostProperties)
         val callback = FakeCallbackInternal()
 
@@ -935,30 +934,30 @@
     }
 
     /**
-     * an implementation of CapabilityBuilderBase using Argument. Output, etc. defined under
+     * an implementation of Capability.Builder using Argument. Output, etc. defined under
      * testing/spec
      */
     class CapabilityBuilder :
-        CapabilityBuilderBase<
+        Capability.Builder<
             CapabilityBuilder,
-            Property,
+            Properties,
             Arguments,
             Output,
             Confirmation,
-            Session,
+            ExecutionSession,
             >(ACTION_SPEC) {
 
         init {
             setProperty(SINGLE_REQUIRED_FIELD_PROPERTY)
         }
 
-        override val sessionBridge: SessionBridge<Session, Confirmation> = SessionBridge {
+        override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SessionBridge {
             TaskHandler.Builder<Confirmation>().build()
         }
 
-        public override fun setSessionFactory(
-            sessionFactory: SessionFactory<Session>,
-        ): CapabilityBuilder = super.setSessionFactory(sessionFactory)
+        public override fun setExecutionSessionFactory(
+            sessionFactory: ExecutionSessionFactory<ExecutionSession>,
+        ): CapabilityBuilder = super.setExecutionSessionFactory(sessionFactory)
     }
 
     companion object {
@@ -1015,37 +1014,37 @@
                     return ParamValue.newBuilder().build()
                 }
             }
-        private val ACTION_SPEC: ActionSpec<Property, Arguments, Output> =
+        private val ACTION_SPEC: ActionSpec<Properties, Arguments, Output> =
             ActionSpecBuilder.ofCapabilityNamed(
                 CAPABILITY_NAME,
             )
-                .setDescriptor(Property::class.java)
+                .setDescriptor(Properties::class.java)
                 .setArguments(Arguments::class.java, Arguments::newBuilder)
                 .setOutput(Output::class.java)
                 .bindParameter(
                     "required",
-                    Property::requiredEntityField,
+                    Properties::requiredEntityField,
                     Arguments.Builder::setRequiredEntityField,
                     TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                     TypeConverters.ENTITY_ENTITY_CONVERTER,
                 )
                 .bindOptionalParameter(
                     "optional",
-                    Property::optionalStringField,
+                    Properties::optionalStringField,
                     Arguments.Builder::setOptionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
                 )
                 .bindOptionalParameter(
                     "optionalEnum",
-                    Property::enumField,
+                    Properties::enumField,
                     Arguments.Builder::setEnumField,
                     ENUM_CONVERTER,
                     { Entity.newBuilder().setIdentifier(it.toString()).build() },
                 )
                 .bindRepeatedParameter(
                     "repeated",
-                    Property::repeatedStringField,
+                    Properties::repeatedStringField,
                     Arguments.Builder::setRepeatedStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
@@ -1062,10 +1061,10 @@
                 )
                 .build()
 
-        private val SINGLE_REQUIRED_FIELD_PROPERTY: Property =
-            Property.newBuilder()
+        private val SINGLE_REQUIRED_FIELD_PROPERTY: Properties =
+            Properties.newBuilder()
                 .setRequiredEntityField(
-                    ParamProperty.Builder<
+                    Property.Builder<
                         androidx.appactions.interaction.capabilities.core.properties.Entity,
                         >()
                         .setRequired(true)
@@ -1091,15 +1090,15 @@
          * etc., defined under ../../testing/spec
          */
         private fun <SessionUpdaterT : AbstractTaskUpdater> createCapability(
-            property: Property,
-            sessionFactory: SessionFactory<Session>,
-            sessionBridge: SessionBridge<Session, Confirmation>,
+            property: Properties,
+            sessionFactory: ExecutionSessionFactory<ExecutionSession>,
+            sessionBridge: SessionBridge<ExecutionSession, Confirmation>,
             sessionUpdaterSupplier: Supplier<SessionUpdaterT>,
         ): TaskCapabilityImpl<
-            Property,
+            Properties,
             Arguments,
             Output,
-            Session,
+            ExecutionSession,
             Confirmation,
             SessionUpdaterT,
             > {
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
index 0b0962c..1b8a88e 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
@@ -20,14 +20,14 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.AppEntityListener;
-import androidx.appactions.interaction.capabilities.core.BaseSession;
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.properties.StringValue;
 import androidx.appactions.interaction.capabilities.core.values.ListItem;
 
@@ -39,19 +39,19 @@
 public final class CapabilityStructFill {
 
     private static final String CAPABILITY_NAME = "actions.intent.TEST";
-    public static final ActionSpec<Property, Arguments, Void> ACTION_SPEC =
+    public static final ActionSpec<Properties, Arguments, Void> ACTION_SPEC =
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-                    .setDescriptor(Property.class)
+                    .setDescriptor(Properties.class)
                     .setArguments(Arguments.class, Arguments::newBuilder)
                     .bindOptionalParameter(
                             "listItem",
-                            Property::listItem,
+                            Properties::listItem,
                             Arguments.Builder::setListItem,
                             ParamValueConverter.Companion.of(LIST_ITEM_TYPE_SPEC),
                             EntityConverter.Companion.of(LIST_ITEM_TYPE_SPEC)::convert)
                     .bindOptionalParameter(
                             "string",
-                            Property::anyString,
+                            Properties::anyString,
                             Arguments.Builder::setAnyString,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
@@ -86,32 +86,32 @@
 
     /** Two required strings */
     @AutoValue
-    public abstract static class Property {
+    public abstract static class Properties {
         @NonNull
         public static Builder newBuilder() {
-            return new AutoValue_CapabilityStructFill_Property.Builder();
+            return new AutoValue_CapabilityStructFill_Properties.Builder();
         }
 
-        public abstract Optional<ParamProperty<ListItem>> listItem();
+        public abstract Optional<Property<ListItem>> listItem();
 
-        public abstract Optional<ParamProperty<StringValue>> anyString();
+        public abstract Optional<Property<StringValue>> anyString();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setListItem(@NonNull ParamProperty<ListItem> value);
+            public abstract Builder setListItem(@NonNull Property<ListItem> value);
 
             @NonNull
-            public abstract Builder setAnyString(@NonNull ParamProperty<StringValue> value);
+            public abstract Builder setAnyString(@NonNull Property<StringValue> value);
 
             @NonNull
-            public abstract Property build();
+            public abstract Properties build();
         }
     }
 
-    public interface Session extends BaseSession<Arguments, Void> {
+    public interface ExecutionSession extends BaseExecutionSession<Arguments, Void> {
         @NonNull
         AppEntityListener<ListItem> getListItemListener();
     }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
index 3e54c8a..2a4bc3e 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
@@ -17,13 +17,13 @@
 package androidx.appactions.interaction.capabilities.core.testing.spec;
 
 import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.BaseSession;
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
 import androidx.appactions.interaction.capabilities.core.properties.Entity;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.values.EntityValue;
 
 import com.google.auto.value.AutoValue;
@@ -33,19 +33,19 @@
 public final class CapabilityTwoEntityValues {
 
     private static final String CAPABILITY_NAME = "actions.intent.TEST";
-    public static final ActionSpec<Property, Arguments, Void> ACTION_SPEC =
+    public static final ActionSpec<Properties, Arguments, Void> ACTION_SPEC =
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-                    .setDescriptor(Property.class)
+                    .setDescriptor(Properties.class)
                     .setArguments(Arguments.class, Arguments::newBuilder)
                     .bindOptionalParameter(
                             "slotA",
-                            Property::slotA,
+                            Properties::slotA,
                             Arguments.Builder::setSlotA,
                             TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                             TypeConverters.ENTITY_ENTITY_CONVERTER)
                     .bindOptionalParameter(
                             "slotB",
-                            Property::slotB,
+                            Properties::slotB,
                             Arguments.Builder::setSlotB,
                             TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
                             TypeConverters.ENTITY_ENTITY_CONVERTER)
@@ -79,30 +79,30 @@
 
     /** Two required strings */
     @AutoValue
-    public abstract static class Property {
+    public abstract static class Properties {
         @NonNull
         public static Builder newBuilder() {
-            return new AutoValue_CapabilityTwoEntityValues_Property.Builder();
+            return new AutoValue_CapabilityTwoEntityValues_Properties.Builder();
         }
 
-        public abstract Optional<ParamProperty<Entity>> slotA();
+        public abstract Optional<Property<Entity>> slotA();
 
-        public abstract Optional<ParamProperty<Entity>> slotB();
+        public abstract Optional<Property<Entity>> slotB();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setSlotA(@NonNull ParamProperty<Entity> value);
+            public abstract Builder setSlotA(@NonNull Property<Entity> value);
 
             @NonNull
-            public abstract Builder setSlotB(@NonNull ParamProperty<Entity> value);
+            public abstract Builder setSlotB(@NonNull Property<Entity> value);
 
             @NonNull
-            public abstract Property build();
+            public abstract Properties build();
         }
     }
 
-    public interface Session extends BaseSession<Arguments, Void> {}
+    public interface ExecutionSession extends BaseExecutionSession<Arguments, Void> {}
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
index 1c0eaad..20f692d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
@@ -21,7 +21,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.properties.StringValue;
 
 import com.google.auto.value.AutoValue;
@@ -31,19 +31,19 @@
 public final class CapabilityTwoStrings {
 
     private static final String CAPABILITY_NAME = "actions.intent.TEST";
-    public static final ActionSpec<Property, Arguments, Void> ACTION_SPEC =
+    public static final ActionSpec<Properties, Arguments, Void> ACTION_SPEC =
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-                    .setDescriptor(Property.class)
+                    .setDescriptor(Properties.class)
                     .setArguments(Arguments.class, Arguments::newBuilder)
                     .bindOptionalParameter(
                             "stringSlotA",
-                            Property::stringSlotA,
+                            Properties::stringSlotA,
                             Arguments.Builder::setStringSlotA,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
                     .bindOptionalParameter(
                             "stringSlotB",
-                            Property::stringSlotB,
+                            Properties::stringSlotB,
                             Arguments.Builder::setStringSlotB,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
@@ -79,28 +79,28 @@
 
     /** Two required strings */
     @AutoValue
-    public abstract static class Property {
+    public abstract static class Properties {
         @NonNull
         public static Builder newBuilder() {
-            return new AutoValue_CapabilityTwoStrings_Property.Builder();
+            return new AutoValue_CapabilityTwoStrings_Properties.Builder();
         }
 
-        public abstract Optional<ParamProperty<StringValue>> stringSlotA();
+        public abstract Optional<Property<StringValue>> stringSlotA();
 
-        public abstract Optional<ParamProperty<StringValue>> stringSlotB();
+        public abstract Optional<Property<StringValue>> stringSlotB();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setStringSlotA(@NonNull ParamProperty<StringValue> value);
+            public abstract Builder setStringSlotA(@NonNull Property<StringValue> value);
 
             @NonNull
-            public abstract Builder setStringSlotB(@NonNull ParamProperty<StringValue> value);
+            public abstract Builder setStringSlotB(@NonNull Property<StringValue> value);
 
             @NonNull
-            public abstract Property build();
+            public abstract Properties build();
         }
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
similarity index 82%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
index cfebbd5..dbe44b3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
@@ -17,19 +17,19 @@
 package androidx.appactions.interaction.capabilities.core.testing.spec
 
 import androidx.appactions.interaction.capabilities.core.AppEntityListener
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
 import androidx.appactions.interaction.capabilities.core.values.EntityValue
 
-interface Session : BaseSession<Arguments, Output> {
+interface ExecutionSession : BaseExecutionSession<Arguments, Output> {
 
     fun getRequiredEntityListener(): AppEntityListener<EntityValue>? = null
 
     companion object {
         @JvmStatic
-        val DEFAULT: Session = object : Session {
-            override fun onFinishAsync(arguments: Arguments) =
+        val DEFAULT = object : ExecutionSession {
+            override fun onExecuteAsync(arguments: Arguments) =
                 Futures.immediateFuture(
                     ExecutionResult.Builder<Output>().build(),
                 )
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java
similarity index 63%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java
index 65f5bf3..fc9cb05 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java
@@ -19,7 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.properties.Entity;
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.properties.StringValue;
 
 import com.google.auto.value.AutoValue;
@@ -28,34 +28,34 @@
 
 /** Testing implementation of a capability Property. */
 @AutoValue
-public abstract class Property {
+public abstract class Properties {
 
     public static Builder newBuilder() {
-        return new AutoValue_Property.Builder();
+        return new AutoValue_Properties.Builder();
     }
 
-    public abstract ParamProperty<Entity> requiredEntityField();
+    public abstract Property<Entity> requiredEntityField();
 
-    public abstract Optional<ParamProperty<StringValue>> optionalStringField();
+    public abstract Optional<Property<StringValue>> optionalStringField();
 
-    public abstract Optional<ParamProperty<TestEnum>> enumField();
+    public abstract Optional<Property<TestEnum>> enumField();
 
-    public abstract Optional<ParamProperty<StringValue>> repeatedStringField();
+    public abstract Optional<Property<StringValue>> repeatedStringField();
 
     /** Builder for the testing Property. */
     @AutoValue.Builder
-    public abstract static class Builder implements BuilderOf<Property> {
+    public abstract static class Builder implements BuilderOf<Properties> {
 
-        public abstract Builder setRequiredEntityField(ParamProperty<Entity> property);
+        public abstract Builder setRequiredEntityField(Property<Entity> property);
 
-        public abstract Builder setOptionalStringField(ParamProperty<StringValue> property);
+        public abstract Builder setOptionalStringField(Property<StringValue> property);
 
-        public abstract Builder setEnumField(ParamProperty<TestEnum> property);
+        public abstract Builder setEnumField(Property<TestEnum> property);
 
-        public abstract Builder setRepeatedStringField(ParamProperty<StringValue> property);
+        public abstract Builder setRepeatedStringField(Property<StringValue> property);
 
         @NonNull
         @Override
-        public abstract Property build();
+        public abstract Properties build();
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
index fcdeb68..66f19fa 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
@@ -16,14 +16,13 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.time.LocalTime
 import java.util.Optional
 
@@ -33,7 +32,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(GetExerciseObservation.Property::class.java)
+        .setDescriptor(GetExerciseObservation.Properties::class.java)
         .setArguments(
             GetExerciseObservation.Arguments::class.java,
             GetExerciseObservation.Arguments::Builder
@@ -58,15 +57,15 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class GetExerciseObservation private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        private var propertyBuilder: Property.Builder = Property.Builder()
-        fun setStartTimeProperty(startTime: ParamProperty<LocalTime>): CapabilityBuilder = apply {
+        private var propertyBuilder: Properties.Builder = Properties.Builder()
+        fun setStartTimeProperty(startTime: Property<LocalTime>): CapabilityBuilder = apply {
             propertyBuilder.setEndTime(startTime)
         }
 
-        fun setEndTimeProperty(endTime: ParamProperty<LocalTime>): CapabilityBuilder = apply {
+        fun setEndTimeProperty(endTime: Property<LocalTime>): CapabilityBuilder = apply {
             propertyBuilder.setEndTime(endTime)
         }
 
@@ -78,9 +77,9 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val startTime: ParamProperty<LocalTime>?,
-        val endTime: ParamProperty<LocalTime>?
+    class Properties internal constructor(
+        val startTime: Property<LocalTime>?,
+        val endTime: Property<LocalTime>?
     ) {
         override fun toString(): String {
             return "Property(startTime=$startTime, endTime=$endTime)"
@@ -90,7 +89,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (startTime != other.startTime) return false
             if (endTime != other.endTime) return false
@@ -105,16 +104,16 @@
         }
 
         class Builder {
-            private var startTime: ParamProperty<LocalTime>? = null
-            private var endTime: ParamProperty<LocalTime>? = null
+            private var startTime: Property<LocalTime>? = null
+            private var endTime: Property<LocalTime>? = null
 
-            fun setStartTime(startTime: ParamProperty<LocalTime>): Builder =
+            fun setStartTime(startTime: Property<LocalTime>): Builder =
                 apply { this.startTime = startTime }
 
-            fun setEndTime(endTime: ParamProperty<LocalTime>): Builder =
+            fun setEndTime(endTime: Property<LocalTime>): Builder =
                 apply { this.endTime = endTime }
 
-            fun build(): Property = Property(startTime, endTime)
+            fun build(): Properties = Properties(startTime, endTime)
         }
     }
 
@@ -162,5 +161,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
index 31e99d9..df9e344 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
@@ -16,14 +16,13 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.time.LocalTime
 import java.util.Optional
 
@@ -33,7 +32,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(GetHealthObservation.Property::class.java)
+        .setDescriptor(GetHealthObservation.Properties::class.java)
         .setArguments(
             GetHealthObservation.Arguments::class.java,
             GetHealthObservation.Arguments::Builder
@@ -58,15 +57,15 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class GetHealthObservation private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        private var propertyBuilder: Property.Builder = Property.Builder()
-        fun setStartTimeProperty(startTime: ParamProperty<LocalTime>): CapabilityBuilder = apply {
+        private var propertyBuilder: Properties.Builder = Properties.Builder()
+        fun setStartTimeProperty(startTime: Property<LocalTime>): CapabilityBuilder = apply {
             propertyBuilder.setEndTime(startTime)
         }
 
-        fun setEndTimeProperty(endTime: ParamProperty<LocalTime>): CapabilityBuilder = apply {
+        fun setEndTimeProperty(endTime: Property<LocalTime>): CapabilityBuilder = apply {
             propertyBuilder.setEndTime(endTime)
         }
 
@@ -78,9 +77,9 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val startTime: ParamProperty<LocalTime>?,
-        val endTime: ParamProperty<LocalTime>?
+    class Properties internal constructor(
+        val startTime: Property<LocalTime>?,
+        val endTime: Property<LocalTime>?
     ) {
         override fun toString(): String {
             return "Property(startTime=$startTime, endTime=$endTime)"
@@ -90,7 +89,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (startTime != other.startTime) return false
             if (endTime != other.endTime) return false
@@ -105,16 +104,16 @@
         }
 
         class Builder {
-            private var startTime: ParamProperty<LocalTime>? = null
-            private var endTime: ParamProperty<LocalTime>? = null
+            private var startTime: Property<LocalTime>? = null
+            private var endTime: Property<LocalTime>? = null
 
-            fun setStartTime(startTime: ParamProperty<LocalTime>): Builder =
+            fun setStartTime(startTime: Property<LocalTime>): Builder =
                 apply { this.startTime = startTime }
 
-            fun setEndTime(endTime: ParamProperty<LocalTime>): Builder =
+            fun setEndTime(endTime: Property<LocalTime>): Builder =
                 apply { this.endTime = endTime }
 
-            fun build(): Property = Property(startTime, endTime)
+            fun build(): Properties = Properties(startTime, endTime)
         }
     }
 
@@ -162,5 +161,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
index c1ed029..332207f 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
@@ -16,15 +16,14 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.util.Optional
 
 /** PauseExercise.kt in interaction-capabilities-fitness */
@@ -33,7 +32,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(PauseExercise.Property::class.java)
+        .setDescriptor(PauseExercise.Properties::class.java)
         .setArguments(PauseExercise.Arguments::class.java, PauseExercise.Arguments::Builder)
         .setOutput(PauseExercise.Output::class.java)
         .bindOptionalParameter(
@@ -48,11 +47,11 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class PauseExercise private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        private var propertyBuilder: Property.Builder = Property.Builder()
-        fun setNameProperty(name: ParamProperty<StringValue>): CapabilityBuilder =
+        private var propertyBuilder: Properties.Builder = Properties.Builder()
+        fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
             apply {
                 propertyBuilder.setName(name)
             }
@@ -65,8 +64,8 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val name: ParamProperty<StringValue>?,
+    class Properties internal constructor(
+        val name: Property<StringValue>?,
     ) {
         override fun toString(): String {
             return "Property(name=$name)"
@@ -76,7 +75,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (name != other.name) return false
 
@@ -88,12 +87,12 @@
         }
 
         class Builder {
-            private var name: ParamProperty<StringValue>? = null
+            private var name: Property<StringValue>? = null
 
-            fun setName(name: ParamProperty<StringValue>): Builder =
+            fun setName(name: Property<StringValue>): Builder =
                 apply { this.name = name }
 
-            fun build(): Property = Property(name)
+            fun build(): Properties = Properties(name)
         }
     }
 
@@ -133,5 +132,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
index e34c328..491e804 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
@@ -16,15 +16,14 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.util.Optional
 
 /** ResumeExercise.kt in interaction-capabilities-fitness */
@@ -33,7 +32,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(ResumeExercise.Property::class.java)
+        .setDescriptor(ResumeExercise.Properties::class.java)
         .setArguments(ResumeExercise.Arguments::class.java, ResumeExercise.Arguments::Builder)
         .setOutput(ResumeExercise.Output::class.java)
         .bindOptionalParameter(
@@ -48,11 +47,11 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class ResumeExercise private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        private var propertyBuilder: Property.Builder = Property.Builder()
-        fun setNameProperty(name: ParamProperty<StringValue>): CapabilityBuilder =
+        private var propertyBuilder: Properties.Builder = Properties.Builder()
+        fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
             apply {
                 propertyBuilder.setName(name)
             }
@@ -65,8 +64,8 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val name: ParamProperty<StringValue>?,
+    class Properties internal constructor(
+        val name: Property<StringValue>?,
     ) {
         override fun toString(): String {
             return "Property(name=$name)"
@@ -76,7 +75,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (name != other.name) return false
 
@@ -88,12 +87,12 @@
         }
 
         class Builder {
-            private var name: ParamProperty<StringValue>? = null
+            private var name: Property<StringValue>? = null
 
-            fun setName(name: ParamProperty<StringValue>): Builder =
+            fun setName(name: Property<StringValue>): Builder =
                 apply { this.name = name }
 
-            fun build(): Property = Property(name)
+            fun build(): Properties = Properties(name)
         }
     }
 
@@ -133,5 +132,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
index f90f9db..04524b4 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
@@ -16,15 +16,14 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.time.Duration
 import java.util.Optional
 
@@ -34,7 +33,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StartExercise.Property::class.java)
+        .setDescriptor(StartExercise.Properties::class.java)
         .setArguments(StartExercise.Arguments::class.java, StartExercise.Arguments::Builder)
         .setOutput(StartExercise.Output::class.java)
         .bindOptionalParameter(
@@ -56,30 +55,30 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class StartExercise private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        fun setDurationProperty(duration: ParamProperty<Duration>): CapabilityBuilder =
+        fun setDurationProperty(duration: Property<Duration>): CapabilityBuilder =
             apply {
-                Property.Builder().setDuration(duration).build()
+                Properties.Builder().setDuration(duration).build()
             }
 
-        fun setNameProperty(name: ParamProperty<StringValue>): CapabilityBuilder =
+        fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
             apply {
-                Property.Builder().setName(name).build()
+                Properties.Builder().setName(name).build()
             }
 
         override fun build(): Capability {
             // TODO(b/268369632): No-op remove empty property builder after Property od removed
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val duration: ParamProperty<Duration>?,
-        val name: ParamProperty<StringValue>?
+    class Properties internal constructor(
+        val duration: Property<Duration>?,
+        val name: Property<StringValue>?
     ) {
         override fun toString(): String {
             return "Property(duration=$duration, name=$name)"
@@ -89,7 +88,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (duration != other.duration) return false
             if (name != other.name) return false
@@ -104,16 +103,16 @@
         }
 
         class Builder {
-            private var duration: ParamProperty<Duration>? = null
-            private var name: ParamProperty<StringValue>? = null
+            private var duration: Property<Duration>? = null
+            private var name: Property<StringValue>? = null
 
-            fun setDuration(duration: ParamProperty<Duration>): Builder =
+            fun setDuration(duration: Property<Duration>): Builder =
                 apply { this.duration = duration }
 
-            fun setName(name: ParamProperty<StringValue>): Builder =
+            fun setName(name: Property<StringValue>): Builder =
                 apply { this.name = name }
 
-            fun build(): Property = Property(duration, name)
+            fun build(): Properties = Properties(duration, name)
         }
     }
 
@@ -161,5 +160,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
index a646559..09690c52 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
@@ -16,14 +16,13 @@
 
 package androidx.appactions.interaction.capabilities.fitness.fitness
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
 import java.util.Optional
 
@@ -33,7 +32,7 @@
 // TODO(b/273602015): Update to use Name property from builtintype library.
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StopExercise.Property::class.java)
+        .setDescriptor(StopExercise.Properties::class.java)
         .setArguments(StopExercise.Arguments::class.java, StopExercise.Arguments::Builder)
         .setOutput(StopExercise.Output::class.java)
         .bindOptionalParameter(
@@ -48,11 +47,11 @@
 @CapabilityFactory(name = CAPABILITY_NAME)
 class StopExercise private constructor() {
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
-        private var propertyBuilder: Property.Builder = Property.Builder()
-        fun setNameProperty(name: ParamProperty<StringValue>): CapabilityBuilder =
+        private var propertyBuilder: Properties.Builder = Properties.Builder()
+        fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
             apply {
                 propertyBuilder.setName(name)
             }
@@ -65,8 +64,8 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val name: ParamProperty<StringValue>?,
+    class Properties internal constructor(
+        val name: Property<StringValue>?,
     ) {
         override fun toString(): String {
             return "Property(name=$name)"
@@ -76,7 +75,7 @@
             if (this === other) return true
             if (javaClass !== other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (name != other.name) return false
 
@@ -88,12 +87,12 @@
         }
 
         class Builder {
-            private var name: ParamProperty<StringValue>? = null
+            private var name: Property<StringValue>? = null
 
-            fun setName(name: ParamProperty<StringValue>): Builder =
+            fun setName(name: Property<StringValue>): Builder =
                 apply { this.name = name }
 
-            fun build(): Property = Property(name)
+            fun build(): Properties = Properties(name)
         }
     }
 
@@ -133,5 +132,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
index fb37e78..f4a5294 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
@@ -17,12 +17,11 @@
 package androidx.appactions.interaction.capabilities.productivity
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
 import androidx.appactions.interaction.proto.ParamValue
@@ -35,7 +34,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(PauseTimer.Property::class.java)
+        .setDescriptor(PauseTimer.Properties::class.java)
         .setArguments(PauseTimer.Arguments::class.java, PauseTimer.Arguments::Builder)
         .setOutput(PauseTimer.Output::class.java)
         .bindRepeatedParameter(
@@ -56,24 +55,24 @@
 class PauseTimer private constructor() {
 
     class CapabilityBuilder :
-        CapabilityBuilderBase<
+        Capability.Builder<
             CapabilityBuilder,
-            Property,
+            Properties,
             Arguments,
             Output,
             Confirmation,
-            Session,
+            ExecutionSession,
         >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property
+    class Properties
     internal constructor(
-        val timerList: ParamProperty<TimerValue>?,
+        val timerList: Property<TimerValue>?,
     ) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
@@ -83,7 +82,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (timerList != other.timerList) return false
 
@@ -95,13 +94,13 @@
         }
 
         class Builder {
-            private var timerList: ParamProperty<TimerValue>? = null
+            private var timerList: Property<TimerValue>? = null
 
-            fun setTimerList(timerList: ParamProperty<TimerValue>): Builder = apply {
+            fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
                 this.timerList = timerList
             }
 
-            fun build(): Property = Property(timerList)
+            fun build(): Properties = Properties(timerList)
         }
     }
 
@@ -201,5 +200,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
index 58a0cc3..ec8fea6 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
@@ -17,12 +17,11 @@
 package androidx.appactions.interaction.capabilities.productivity
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
 import androidx.appactions.interaction.proto.ParamValue
@@ -35,7 +34,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(ResetTimer.Property::class.java)
+        .setDescriptor(ResetTimer.Properties::class.java)
         .setArguments(ResetTimer.Arguments::class.java, ResetTimer.Arguments::Builder)
         .setOutput(ResetTimer.Output::class.java)
         .bindRepeatedParameter(
@@ -56,17 +55,17 @@
 class ResetTimer private constructor() {
 
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
         >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(val timerList: ParamProperty<TimerValue>?) {
+    class Properties internal constructor(val timerList: Property<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -75,7 +74,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (timerList != other.timerList) return false
 
@@ -87,13 +86,13 @@
         }
 
         class Builder {
-            private var timerList: ParamProperty<TimerValue>? = null
+            private var timerList: Property<TimerValue>? = null
 
-            fun setTimerList(timerList: ParamProperty<TimerValue>): Builder = apply {
+            fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
                 this.timerList = timerList
             }
 
-            fun build(): Property = Property(timerList)
+            fun build(): Properties = Properties(timerList)
         }
     }
 
@@ -190,5 +189,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
index bee3d8f..d8b2fb0 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
@@ -17,12 +17,11 @@
 package androidx.appactions.interaction.capabilities.productivity
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
 import androidx.appactions.interaction.proto.ParamValue
@@ -35,7 +34,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(ResumeTimer.Property::class.java)
+        .setDescriptor(ResumeTimer.Properties::class.java)
         .setArguments(ResumeTimer.Arguments::class.java, ResumeTimer.Arguments::Builder)
         .setOutput(ResumeTimer.Output::class.java)
         .bindRepeatedParameter(
@@ -56,17 +55,17 @@
 class ResumeTimer private constructor() {
 
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
         >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(val timerList: ParamProperty<TimerValue>?) {
+    class Properties internal constructor(val timerList: Property<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -75,7 +74,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (timerList != other.timerList) return false
 
@@ -87,13 +86,13 @@
         }
 
         class Builder {
-            private var timerList: ParamProperty<TimerValue>? = null
+            private var timerList: Property<TimerValue>? = null
 
-            fun setTimerList(timerList: ParamProperty<TimerValue>): Builder = apply {
+            fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
                 this.timerList = timerList
             }
 
-            fun build(): Property = Property(timerList)
+            fun build(): Properties = Properties(timerList)
         }
     }
 
@@ -190,5 +189,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
index bcf5458..360b356 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
@@ -17,15 +17,14 @@
 package androidx.appactions.interaction.capabilities.productivity
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
-import androidx.appactions.interaction.capabilities.core.SessionFactory
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
 import androidx.appactions.interaction.capabilities.core.ValueListener
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
 import androidx.appactions.interaction.capabilities.core.impl.task.TaskHandler
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
@@ -41,7 +40,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StartTimer.Property::class.java)
+        .setDescriptor(StartTimer.Properties::class.java)
         .setArguments(StartTimer.Arguments::class.java, StartTimer.Arguments::Builder)
         .setOutput(StartTimer.Output::class.java)
         .bindOptionalParameter(
@@ -72,7 +71,7 @@
         )
         .build()
 
-private val SESSION_BRIDGE = SessionBridge<StartTimer.Session, StartTimer.Confirmation> {
+private val SESSION_BRIDGE = SessionBridge<StartTimer.ExecutionSession, StartTimer.Confirmation> {
         session ->
     val taskHandlerBuilder = TaskHandler.Builder<StartTimer.Confirmation>()
     session.nameListener?.let {
@@ -96,23 +95,23 @@
 class StartTimer private constructor() {
 
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session,
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
             >(ACTION_SPEC) {
 
-        override val sessionBridge: SessionBridge<Session, Confirmation> = SESSION_BRIDGE
+        override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SESSION_BRIDGE
 
-        public override fun setSessionFactory(
-            sessionFactory: SessionFactory<Session>,
-        ): CapabilityBuilder = super.setSessionFactory(sessionFactory)
+        public override fun setExecutionSessionFactory(
+            sessionFactory: ExecutionSessionFactory<ExecutionSession>,
+        ): CapabilityBuilder = super.setExecutionSessionFactory(sessionFactory)
 
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
-    interface Session : BaseSession<Arguments, Output> {
+    interface ExecutionSession : BaseExecutionSession<Arguments, Output> {
         val nameListener: ValueListener<String>?
             get() = null
         val durationListener: ValueListener<Duration>?
@@ -120,11 +119,11 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property
+    class Properties
     internal constructor(
-        val identifier: ParamProperty<StringValue>?,
-        val name: ParamProperty<StringValue>?,
-        val duration: ParamProperty<Duration>?,
+        val identifier: Property<StringValue>?,
+        val name: Property<StringValue>?,
+        val duration: Property<Duration>?,
     ) {
         override fun toString(): String {
             return "Property(identifier=$identifier,name=$name,duration=$duration}"
@@ -134,7 +133,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (identifier != other.identifier) return false
             if (name != other.name) return false
@@ -151,21 +150,21 @@
         }
 
         class Builder {
-            private var identifier: ParamProperty<StringValue>? = null
-            private var name: ParamProperty<StringValue>? = null
-            private var duration: ParamProperty<Duration>? = null
+            private var identifier: Property<StringValue>? = null
+            private var name: Property<StringValue>? = null
+            private var duration: Property<Duration>? = null
 
-            fun setIdentifier(identifier: ParamProperty<StringValue>): Builder = apply {
+            fun setIdentifier(identifier: Property<StringValue>): Builder = apply {
                 this.identifier = identifier
             }
 
-            fun setName(name: ParamProperty<StringValue>): Builder = apply { this.name = name }
+            fun setName(name: Property<StringValue>): Builder = apply { this.name = name }
 
-            fun setDuration(duration: ParamProperty<Duration>): Builder = apply {
+            fun setDuration(duration: Property<Duration>): Builder = apply {
                 this.duration = duration
             }
 
-            fun build(): Property = Property(identifier, name, duration)
+            fun build(): Properties = Properties(identifier, name, duration)
         }
     }
 
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
index e0797b3..c018c60 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
@@ -17,12 +17,11 @@
 package androidx.appactions.interaction.capabilities.productivity
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
 import androidx.appactions.interaction.proto.ParamValue
@@ -35,7 +34,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StopTimer.Property::class.java)
+        .setDescriptor(StopTimer.Properties::class.java)
         .setArguments(StopTimer.Arguments::class.java, StopTimer.Arguments::Builder)
         .setOutput(StopTimer.Output::class.java)
         .bindRepeatedParameter(
@@ -56,17 +55,17 @@
 class StopTimer private constructor() {
 
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
-        >(ACTION_SPEC) {
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+            >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(val timerList: ParamProperty<TimerValue>?) {
+    class Properties internal constructor(val timerList: Property<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -75,7 +74,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (timerList != other.timerList) return false
 
@@ -87,13 +86,13 @@
         }
 
         class Builder {
-            private var timerList: ParamProperty<TimerValue>? = null
+            private var timerList: Property<TimerValue>? = null
 
-            fun setTimerList(timerList: ParamProperty<TimerValue>): Builder = apply {
+            fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
                 this.timerList = timerList
             }
 
-            fun build(): Property = Property(timerList)
+            fun build(): Properties = Properties(timerList)
         }
     }
 
@@ -190,5 +189,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
index 7dd4520..79e0bd3 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
@@ -16,9 +16,8 @@
 
 package androidx.appactions.interaction.capabilities.safety
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -38,7 +37,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StartEmergencySharing.Property::class.java)
+        .setDescriptor(StartEmergencySharing.Properties::class.java)
         .setArguments(
             StartEmergencySharing.Arguments::class.java,
             StartEmergencySharing.Arguments::Builder
@@ -55,17 +54,17 @@
 class StartEmergencySharing private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session,
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
             >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property())
+            super.setProperty(Properties())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor()
+    class Properties internal constructor()
 
     class Arguments internal constructor() {
         class Builder : BuilderOf<Arguments> {
@@ -168,5 +167,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
index 3b924c5..0bf2a33 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
@@ -17,14 +17,13 @@
 package androidx.appactions.interaction.capabilities.safety
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.SAFETY_CHECK_TYPE_SPEC
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SafetyCheck
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
@@ -45,7 +44,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StartSafetyCheck.Property::class.java)
+        .setDescriptor(StartSafetyCheck.Properties::class.java)
         .setArguments(StartSafetyCheck.Arguments::class.java, StartSafetyCheck.Arguments::Builder)
         .setOutput(StartSafetyCheck.Output::class.java)
         .bindOptionalParameter(
@@ -78,20 +77,20 @@
 class StartSafetyCheck private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
         override fun build(): Capability {
             // TODO(b/268369632): No-op remove empty property builder after Property od removed
-            super.setProperty(Property.Builder().build())
+            super.setProperty(Properties.Builder().build())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val duration: ParamProperty<Duration>?,
-        val checkInTime: ParamProperty<ZonedDateTime>?
+    class Properties internal constructor(
+        val duration: Property<Duration>?,
+        val checkInTime: Property<ZonedDateTime>?
     ) {
         override fun toString(): String {
             return "Property(duration=$duration, checkInTime=$checkInTime)"
@@ -101,7 +100,7 @@
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
-            other as Property
+            other as Properties
 
             if (duration != other.duration) return false
             if (checkInTime != other.checkInTime) return false
@@ -116,17 +115,17 @@
         }
 
         class Builder {
-            private var duration: ParamProperty<Duration>? = null
+            private var duration: Property<Duration>? = null
 
-            private var checkInTime: ParamProperty<ZonedDateTime>? = null
+            private var checkInTime: Property<ZonedDateTime>? = null
 
-            fun setDuration(duration: ParamProperty<Duration>): Builder =
+            fun setDuration(duration: Property<Duration>): Builder =
                 apply { this.duration = duration }
 
-            fun setCheckInTime(checkInTime: ParamProperty<ZonedDateTime>): Builder =
+            fun setCheckInTime(checkInTime: Property<ZonedDateTime>): Builder =
                 apply { this.checkInTime = checkInTime }
 
-            fun build(): Property = Property(duration, checkInTime)
+            fun build(): Properties = Properties(duration, checkInTime)
         }
     }
 
@@ -285,5 +284,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
index f3dd520..1224831 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
@@ -16,9 +16,8 @@
 
 package androidx.appactions.interaction.capabilities.safety
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -38,7 +37,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StopEmergencySharing.Property::class.java)
+        .setDescriptor(StopEmergencySharing.Properties::class.java)
         .setArguments(
             StopEmergencySharing.Arguments::class.java,
             StopEmergencySharing.Arguments::Builder
@@ -55,17 +54,17 @@
 class StopEmergencySharing private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session,
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
             >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property())
+            super.setProperty(Properties())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor()
+    class Properties internal constructor()
 
     class Arguments internal constructor() {
         class Builder : BuilderOf<Arguments> {
@@ -168,5 +167,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
index 3f4cf8c..01d3ff9 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
@@ -16,9 +16,8 @@
 
 package androidx.appactions.interaction.capabilities.safety
 
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -38,7 +37,7 @@
 
 private val ACTION_SPEC =
     ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setDescriptor(StopSafetyCheck.Property::class.java)
+        .setDescriptor(StopSafetyCheck.Properties::class.java)
         .setArguments(StopSafetyCheck.Arguments::class.java, StopSafetyCheck.Arguments::Builder)
         .setOutput(StopSafetyCheck.Output::class.java)
         .bindOptionalOutput(
@@ -52,17 +51,17 @@
 class StopSafetyCheck private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
-        CapabilityBuilderBase<
-            CapabilityBuilder, Property, Arguments, Output, Confirmation, Session
+        Capability.Builder<
+            CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
             >(ACTION_SPEC) {
         override fun build(): Capability {
-            super.setProperty(Property())
+            super.setProperty(Properties())
             return super.build()
         }
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor()
+    class Properties internal constructor()
 
     class Arguments internal constructor() {
         class Builder : BuilderOf<Arguments> {
@@ -165,5 +164,5 @@
 
     class Confirmation internal constructor()
 
-    sealed interface Session : BaseSession<Arguments, Output>
+    sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
 }
diff --git a/appactions/interaction/interaction-service/build.gradle b/appactions/interaction/interaction-service/build.gradle
index 0f38bdc..3c69353 100644
--- a/appactions/interaction/interaction-service/build.gradle
+++ b/appactions/interaction/interaction-service/build.gradle
@@ -44,6 +44,7 @@
     implementation(libs.grpcAndroid)
     implementation(libs.grpcBinder)
     implementation(libs.grpcStub)
+    implementation(libs.kotlinCoroutinesCore)
     implementation(libs.kotlinStdlib)
     implementation(libs.jsr250)
 
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionService.kt b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionService.kt
index 1c260b8..623fc0f 100644
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionService.kt
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionService.kt
@@ -22,6 +22,7 @@
 import android.util.Log
 import androidx.annotation.CallSuper
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.entity.EntityProvider
 import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc
 import io.grpc.Server
 import io.grpc.binder.AndroidComponentAddress
@@ -47,6 +48,13 @@
     abstract val registeredCapabilities: List<Capability>
 
     /**
+     * Called by the system once after the Assistant binds to the service.
+     *
+     * @return the list of EntityProvider that this service supports.
+     */
+    abstract val registeredEntityProviders: List<EntityProvider<*>>
+
+    /**
      * A list of [AppVerificationInfo] which define who is allowed to interact with the app's bound
      * service. This gives control over which clients are allowed to communicate with the service.
      *
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java
deleted file mode 100644
index f4aaf5d..0000000
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.service;
-
-import android.util.Log;
-import android.util.SizeF;
-import android.widget.RemoteViews;
-import android.widget.RemoteViewsService.RemoteViewsFactory;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.Capability;
-import androidx.appactions.interaction.capabilities.core.HostProperties;
-import androidx.appactions.interaction.capabilities.core.LibInfo;
-import androidx.appactions.interaction.capabilities.core.impl.ArgumentsWrapper;
-import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession;
-import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal;
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.FutureCallback;
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures;
-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.FulfillmentRequest;
-import androidx.appactions.interaction.proto.FulfillmentResponse;
-import androidx.appactions.interaction.proto.GroundingRequest;
-import androidx.appactions.interaction.proto.GroundingResponse;
-import androidx.appactions.interaction.proto.Version;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc.AppInteractionServiceImplBase;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.CollectionRequest;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.CollectionResponse;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.RemoteViewsInfo;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Request;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Response;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionRequest;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionResponse;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Status.Code;
-import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.UiUpdate;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import io.grpc.Status;
-import io.grpc.StatusException;
-import io.grpc.StatusRuntimeException;
-import io.grpc.stub.StreamObserver;
-
-/**
- * Implementation of {@link AppInteractionServiceImplBase} generated from the GRPC proto file. This
- * class delegates the requests to the appropriate capability session.
- */
-final class AppInteractionServiceGrpcImpl extends AppInteractionServiceImplBase {
-
-    private static final String TAG = "ActionsServiceGrpcImpl";
-
-    static final String ERROR_NO_SESSION = "Session not available";
-    static final String ERROR_NO_FULFILLMENT_REQUEST = "Fulfillment request missing";
-    static final String ERROR_NO_ACTION_CAPABILITY = "Capability was not found";
-    static final String ERROR_SESSION_ENDED = "Session already ended";
-    private static final String ERROR_NO_COLLECTION_SUPPORT =
-            "Session doesn't support collection view";
-    private static final String ERROR_NO_UI = "No UI set";
-    private static final String ERROR_MULTIPLE_UI_TYPES =
-            "Multiple UI types used in current session";
-
-    final AppInteractionService mAppInteractionService;
-    List<Capability> mRegisteredCapabilities = new ArrayList<>();
-
-    static {
-        LoggerInternal.setLogger(
-                new CapabilityLogger() {
-                    @Override
-                    public void log(
-                            @NonNull LogLevel logLevel,
-                            @NonNull String logTag,
-                            @NonNull String message) {
-                        switch (logLevel) {
-                            case ERROR:
-                                Log.e(logTag, message);
-                                break;
-                            case WARN:
-                                Log.w(logTag, message);
-                                break;
-                            case INFO:
-                                Log.i(logTag, message);
-                                break;
-                        }
-                    }
-
-                    @Override
-                    public void log(
-                            @NonNull LogLevel logLevel,
-                            @NonNull String logTag,
-                            @NonNull String message,
-                            @NonNull Throwable throwable) {
-                        switch (logLevel) {
-                            case ERROR:
-                                Log.e(logTag, message, throwable);
-                                break;
-                            case WARN:
-                                Log.w(logTag, message, throwable);
-                                break;
-                            case INFO:
-                                Log.i(logTag, message, throwable);
-                                break;
-                        }
-                    }
-                });
-    }
-
-    AppInteractionServiceGrpcImpl(AppInteractionService mAppInteractionService) {
-        this.mAppInteractionService = mAppInteractionService;
-    }
-
-    @Override
-    public StreamObserver<StartSessionRequest> startUpSession(
-            StreamObserver<StartSessionResponse> responseObserver) {
-        return new StartSessionRequestObserver(responseObserver);
-    }
-
-    private final class StartSessionRequestObserver implements StreamObserver<StartSessionRequest> {
-        private final StreamObserver<StartSessionResponse> mStartSessionResponseObserver;
-        // Every AppInteractionService connection is defined by this streaming RPC connection.
-        // There should only be one session tied to each gRPC impl instance.
-        private String mCurrentSessionId = null;
-
-        StartSessionRequestObserver(StreamObserver<StartSessionResponse> responseObserver) {
-            this.mStartSessionResponseObserver = responseObserver;
-        }
-
-        @Override
-        public void onNext(StartSessionRequest request) {
-            if (mCurrentSessionId != null) {
-                return;
-            }
-            mCurrentSessionId = request.getSessionIdentifier();
-            if (mRegisteredCapabilities.isEmpty()) {
-                mRegisteredCapabilities = mAppInteractionService.getRegisteredCapabilities();
-            }
-            Optional<Capability> targetCapability =
-                    mRegisteredCapabilities.stream()
-                            .filter(cap -> request.getIdentifier().equals(cap.getId()))
-                            .findFirst();
-            if (!targetCapability.isPresent()) {
-                mStartSessionResponseObserver.onError(
-                        new StatusRuntimeException(
-                                Status.FAILED_PRECONDITION.withDescription(
-                                        ERROR_NO_ACTION_CAPABILITY)));
-                return;
-            }
-            HostProperties hostProperties =
-                    new HostProperties.Builder()
-                            .setMaxHostSizeDp(new SizeF(
-                                    request.getHostProperties().getHostViewHeightDp(),
-                                    request.getHostProperties().getHostViewWidthDp()))
-                            .build();
-            CapabilitySession session = targetCapability.get().createSession(
-                    mCurrentSessionId, hostProperties);
-            SessionManager.INSTANCE.putSession(mCurrentSessionId, session);
-            mStartSessionResponseObserver.onNext(StartSessionResponse.getDefaultInstance());
-        }
-
-        @Override
-        public void onError(Throwable t) {
-            synchronized (mStartSessionResponseObserver) {
-                mStartSessionResponseObserver.onError(t);
-            }
-            if (mCurrentSessionId != null) {
-                destroySession(mCurrentSessionId);
-            }
-            mCurrentSessionId = null;
-        }
-
-        @Override
-        public void onCompleted() {
-            synchronized (mStartSessionResponseObserver) {
-                mStartSessionResponseObserver.onCompleted();
-            }
-            if (mCurrentSessionId != null) {
-                destroySession(mCurrentSessionId);
-            }
-            mCurrentSessionId = null;
-        }
-    }
-
-    @Override
-    public void sendRequestFulfillment(Request request, StreamObserver<Response> responseObserver) {
-        if (request.getFulfillmentRequest().getFulfillmentsList().isEmpty()) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(
-                                    ERROR_NO_FULFILLMENT_REQUEST)));
-            return;
-        }
-        FulfillmentRequest.Fulfillment selectedFulfillment =
-                request.getFulfillmentRequest().getFulfillments(0);
-        Optional<Capability> capability =
-                mRegisteredCapabilities.stream()
-                        .filter(cap -> selectedFulfillment.getIdentifier().equals(cap.getId()))
-                        .findFirst();
-        if (!capability.isPresent()) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(
-                                    ERROR_NO_ACTION_CAPABILITY)));
-            return;
-        }
-        String sessionId = request.getSessionIdentifier();
-        CapabilitySession currentSession = SessionManager.INSTANCE.getSession(sessionId);
-        if (currentSession == null) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION)));
-            return;
-        }
-        if (currentSession.getStatus() == CapabilitySession.Status.COMPLETED
-                || currentSession.getStatus() == CapabilitySession.Status.DESTROYED) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED)));
-            return;
-        }
-        Futures.addCallback(
-                executeFulfillmentRequest(currentSession, selectedFulfillment),
-                new FutureCallback<FulfillmentResponse>() {
-                    @Override
-                    public void onSuccess(FulfillmentResponse fulfillmentResponse) {
-                        Response.Builder responseBuilder =
-                                convertFulfillmentResponse(fulfillmentResponse, capability.get())
-                                        .toBuilder();
-                        UiCache uiCache = UiSessions.INSTANCE.getUiCacheOrNull(sessionId);
-                        if (uiCache != null && uiCache.hasUnreadUiResponse()) {
-                            responseBuilder.setUiUpdate(UiUpdate.getDefaultInstance());
-                            if (!uiCache.getCachedChangedViewIds().isEmpty()) {
-                                responseBuilder.setCollectionUpdate(
-                                        AppInteractionServiceProto.CollectionUpdate.newBuilder()
-                                                .addAllViewIds(uiCache.getCachedChangedViewIds()));
-                            }
-                            uiCache.resetUnreadUiResponse();
-                        }
-                        respondAndComplete(responseBuilder.build(), responseObserver);
-                    }
-
-                    @Override
-                    public void onFailure(@NonNull Throwable t) {
-                        Throwable outputThrowable;
-                        if (t instanceof CapabilityExecutionException) {
-                            outputThrowable =
-                                    convertToGrpcException((CapabilityExecutionException) t);
-                        } else if (t instanceof StatusRuntimeException
-                                || t instanceof StatusException) {
-                            outputThrowable = t;
-                        } else {
-                            outputThrowable =
-                                    new StatusRuntimeException(
-                                            Status.INTERNAL.withDescription(
-                                                    t.getMessage()).withCause(t));
-                        }
-                        responseObserver.onError(outputThrowable);
-                        // Assistant will terminate the connection, which will reach
-                        // startUpSession.onError(t) / onCompleted()
-                    }
-                },
-                Runnable::run);
-    }
-
-    @Override
-    public void requestUi(
-            AppInteractionServiceProto.UiRequest req,
-            StreamObserver<AppInteractionServiceProto.UiResponse> responseObserver) {
-        String sessionId = req.getSessionIdentifier();
-        CapabilitySession currentSession = SessionManager.INSTANCE
-                .getSession(sessionId);
-        if (currentSession == null) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION)));
-            return;
-        }
-        if (currentSession.getStatus() == CapabilitySession.Status.COMPLETED) {
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED)));
-            return;
-        }
-        UiCache uiCache = UiSessions.INSTANCE.getUiCacheOrNull(sessionId);
-        if (uiCache == null) {
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)));
-            return;
-        }
-
-        TileLayoutInternal tileLayout = uiCache.getCachedTileLayout();
-        SizeF remoteViewsSize = uiCache.getCachedRemoteViewsSize();
-        RemoteViews remoteViews = uiCache.getCachedRemoteViews();
-        if (tileLayout != null && remoteViews != null) {
-            // TODO(b/272379825): Decide if this is really an invalid state.
-            // both types of UI are present, this is a misused of API. We will treat it as error.
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.INTERNAL.withDescription(ERROR_MULTIPLE_UI_TYPES)));
-            return;
-        }
-        if (tileLayout != null) {
-            respondAndComplete(
-                    AppInteractionServiceProto.UiResponse.newBuilder()
-                            .setTileLayout(tileLayout.toProto())
-                            .build(),
-                    responseObserver);
-            return;
-        }
-        if (remoteViews != null && remoteViewsSize != null) {
-            RemoteViewsOverMetadataInterceptor.setRemoteViews(remoteViews);
-            respondAndComplete(
-                    AppInteractionServiceProto.UiResponse.newBuilder()
-                            .setRemoteViewsInfo(
-                                    RemoteViewsInfo.newBuilder()
-                                            .setWidthDp(remoteViewsSize.getWidth())
-                                            .setHeightDp(remoteViewsSize.getHeight()))
-                            .build(),
-                    responseObserver);
-            return;
-        }
-        destroySession(req.getSessionIdentifier());
-        responseObserver.onError(
-                new StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)));
-    }
-
-    @Override
-    public void requestCollection(
-            CollectionRequest req, StreamObserver<CollectionResponse> responseObserver) {
-        String sessionId = req.getSessionIdentifier();
-        CapabilitySession currentSession = SessionManager.INSTANCE
-                .getSession(sessionId);
-        if (currentSession == null) {
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION)));
-            return;
-        }
-        if (currentSession.getStatus() == CapabilitySession.Status.COMPLETED) {
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED)));
-            return;
-        }
-        UiCache uiCache = UiSessions.INSTANCE.getUiCacheOrNull(sessionId);
-        if (uiCache == null) {
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)));
-            return;
-        }
-        RemoteViewsFactory factory = uiCache.onGetViewFactoryInternal(req.getViewId());
-        if (factory == null) {
-            destroySession(req.getSessionIdentifier());
-            responseObserver.onError(
-                    new StatusRuntimeException(
-                            Status.UNIMPLEMENTED.withDescription(ERROR_NO_COLLECTION_SUPPORT)));
-            return;
-        }
-        switch (req.getRequestDataCase()) {
-            case ON_DESTROY: {
-                requestCollectionOnDestroy(factory, responseObserver);
-                break;
-            }
-            case GET_COUNT: {
-                requestCollectionGetCount(factory, responseObserver);
-                break;
-            }
-            case GET_VIEW_AT: {
-                requestCollectionGetViewAt(factory, responseObserver,
-                        req.getGetViewAt().getPosition());
-                break;
-            }
-
-            case GET_LOADING_VIEW: {
-                requestCollectionGetLoadingView(factory, responseObserver);
-                break;
-            }
-            case GET_VIEW_TYPE_COUNT: {
-                requestCollectionGetViewTypeCount(factory, responseObserver);
-                break;
-            }
-            case GET_ITEM_ID: {
-                requestCollectionGetItemId(factory, responseObserver,
-                        req.getGetItemId().getPosition());
-                break;
-            }
-            case HAS_STABLE_IDS: {
-                requestCollectionHasStableIds(factory, responseObserver);
-                break;
-            }
-            default: {
-                // ignore it
-                Log.d(TAG, "received CollectionRequest with unknown RequestData case.");
-                responseObserver.onCompleted();
-                break;
-            }
-        }
-    }
-
-    @Override
-    public void requestGrounding(
-            GroundingRequest request, StreamObserver<GroundingResponse> responseObserver) {
-        // TODO(b/268265068): Implement grounding API
-    }
-
-    private void requestCollectionOnDestroy(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer) {
-        factory.onDestroy();
-        respondAndComplete(CollectionResponse.getDefaultInstance(), observer);
-    }
-
-    private void requestCollectionGetCount(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer) {
-        respondAndComplete(
-                CollectionResponse.newBuilder()
-                        .setGetCount(CollectionResponse.GetCount.newBuilder()
-                                .setCount(factory.getCount()))
-                        .build(),
-                observer);
-    }
-
-    private void requestCollectionGetViewAt(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer, int position) {
-        RemoteViews view = factory.getViewAt(position);
-        if (view != null) {
-            RemoteViewsOverMetadataInterceptor.setRemoteViews(view);
-        }
-        respondAndComplete(CollectionResponse.getDefaultInstance(), observer);
-    }
-
-    private void requestCollectionGetLoadingView(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer) {
-        RemoteViews loadingView = factory.getLoadingView();
-        if (loadingView != null) {
-            RemoteViewsOverMetadataInterceptor.setRemoteViews(loadingView);
-        }
-        respondAndComplete(CollectionResponse.getDefaultInstance(), observer);
-    }
-
-    private void requestCollectionGetViewTypeCount(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer) {
-        respondAndComplete(
-                CollectionResponse.newBuilder()
-                        .setGetViewTypeCount(
-                                CollectionResponse.GetViewTypeCount.newBuilder()
-                                        .setViewTypeCount(factory.getViewTypeCount()))
-                        .build(),
-                observer);
-    }
-
-    private void requestCollectionGetItemId(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer, int position) {
-        respondAndComplete(
-                CollectionResponse.newBuilder()
-                        .setGetItemId(
-                                CollectionResponse.GetItemId.newBuilder()
-                                        .setItemId(factory.getItemId(position)))
-                        .build(),
-                observer);
-    }
-
-    private void requestCollectionHasStableIds(
-            RemoteViewsFactory factory, StreamObserver<CollectionResponse> observer) {
-        respondAndComplete(
-                CollectionResponse.newBuilder()
-                        .setHasStableIds(
-                                CollectionResponse.HasStableIds.newBuilder()
-                                        .setHasStableIds(factory.hasStableIds()))
-                        .build(),
-                observer);
-    }
-
-    @NonNull
-    private Version convertToAppActionsContextVersion(@NonNull LibInfo.Version libInfoVersion) {
-        Version.Builder builder = Version.newBuilder()
-                .setMajor(libInfoVersion.getMajor())
-                .setMinor(libInfoVersion.getMinor())
-                .setPatch(libInfoVersion.getPatch());
-        if (libInfoVersion.getPreReleaseId() != null) {
-            builder.setPrereleaseId(libInfoVersion.getPreReleaseId());
-        }
-        return builder.build();
-    }
-
-    void destroySession(@NonNull String sessionId) {
-        CapabilitySession session = SessionManager.INSTANCE.getSession(sessionId);
-        if (session != null) {
-            session.destroy();
-        }
-        SessionManager.INSTANCE.removeSession(sessionId);
-    }
-
-    @NonNull
-    StatusRuntimeException convertToGrpcException(CapabilityExecutionException e) {
-        if (e.getErrorStatus() == ErrorStatusInternal.TIMEOUT) {
-            return new StatusRuntimeException(
-                    Status.DEADLINE_EXCEEDED.withDescription(e.getMessage()).withCause(e));
-        }
-        return new StatusRuntimeException(
-                Status.INTERNAL.withDescription(e.getMessage()).withCause(e));
-    }
-
-    @NonNull
-    Response convertFulfillmentResponse(
-            @NonNull FulfillmentResponse fulfillmentResponse,
-            @NonNull Capability capability) {
-        AppActionsContext.AppAction appAction = capability.getAppAction();
-        boolean isDialogSession = appAction.getTaskInfo().getSupportsPartialFulfillment();
-        Version version = convertToAppActionsContextVersion(
-                new LibInfo(mAppInteractionService.getApplicationContext()).getVersion());
-        Response.Builder responseBuilder =
-                // TODO(b/269638788): Add DialogState to the Response proto.
-                Response.newBuilder()
-                        .setFulfillmentResponse(fulfillmentResponse)
-                        .setAppActionsContext(
-                                AppActionsContext.newBuilder()
-                                        .addActions(appAction)
-                                        .setVersion(version)
-                                        .build());
-        if (!isDialogSession) {
-            responseBuilder.setEndingStatus(
-                    AppInteractionServiceProto.Status.newBuilder()
-                            .setStatusCode(Code.COMPLETE)
-                            .build());
-        }
-        return responseBuilder.build();
-    }
-
-    @NonNull
-    ListenableFuture<FulfillmentResponse> executeFulfillmentRequest(
-            @NonNull CapabilitySession session,
-            @NonNull FulfillmentRequest.Fulfillment fulfillmentRequest) {
-        return CallbackToFutureAdapter.getFuture(
-                completer -> {
-                    session.execute(
-                            ArgumentsWrapper.create(fulfillmentRequest),
-                            new CapabilityCallback(completer));
-                    return "executing action capability";
-                });
-    }
-
-    static <T> void respondAndComplete(T response, StreamObserver<T> responseObserver) {
-        responseObserver.onNext(response);
-        responseObserver.onCompleted();
-    }
-}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.kt b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.kt
new file mode 100644
index 0000000..8d5f6a5
--- /dev/null
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.kt
@@ -0,0 +1,624 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.service
+
+import android.util.Log
+import android.util.SizeF
+import android.widget.RemoteViewsService.RemoteViewsFactory
+import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.HostProperties
+import androidx.appactions.interaction.capabilities.core.LibInfo
+import androidx.appactions.interaction.capabilities.core.impl.ArgumentsWrapper
+import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
+import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
+import androidx.appactions.interaction.capabilities.core.impl.concurrent.FutureCallback
+import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
+import androidx.appactions.interaction.capabilities.core.impl.utils.CapabilityLogger
+import androidx.appactions.interaction.capabilities.core.impl.utils.CapabilityLogger.LogLevel
+import androidx.appactions.interaction.capabilities.core.impl.utils.LoggerInternal
+import androidx.appactions.interaction.proto.AppActionsContext
+import androidx.appactions.interaction.proto.FulfillmentRequest
+import androidx.appactions.interaction.proto.FulfillmentResponse
+import androidx.appactions.interaction.proto.GroundingRequest
+import androidx.appactions.interaction.proto.GroundingResponse
+import androidx.appactions.interaction.proto.Version
+import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc.AppInteractionServiceImplBase
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.CollectionRequest
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.CollectionRequest.RequestDataCase
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.CollectionResponse
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.RemoteViewsInfo
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Request
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Response
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionRequest
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionResponse
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Status.Code
+import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.UiUpdate
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import com.google.common.util.concurrent.ListenableFuture
+import io.grpc.Status
+import io.grpc.StatusException
+import io.grpc.StatusRuntimeException
+import io.grpc.stub.StreamObserver
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+/**
+ * Implementation of [AppInteractionServiceImplBase] generated from the GRPC proto file. This
+ * class delegates the requests to the appropriate capability session.
+ */
+internal class AppInteractionServiceGrpcImpl(
+    private val appInteractionService: AppInteractionService,
+) : AppInteractionServiceImplBase() {
+
+    var registeredCapabilities = listOf<Capability>()
+
+    companion object {
+        private const val TAG = "ActionsServiceGrpcImpl"
+        private const val ERROR_NO_COLLECTION_SUPPORT = "Session doesn't support collection view"
+        private const val ERROR_NO_UI = "No UI set"
+        private const val ERROR_MULTIPLE_UI_TYPES = "Multiple UI types used in current session"
+        const val ERROR_NO_SESSION = "Session not available"
+        const val ERROR_NO_FULFILLMENT_REQUEST = "Fulfillment request missing"
+        const val ERROR_NO_ACTION_CAPABILITY = "Capability was not found"
+        const val ERROR_SESSION_ENDED = "Session already ended"
+
+        fun <T> respondAndComplete(response: T, responseObserver: StreamObserver<T>) {
+            synchronized(responseObserver) {
+                responseObserver.onNext(response)
+                responseObserver.onCompleted()
+            }
+        }
+
+        fun respondWithError(t: Throwable, responseObserver: StreamObserver<*>) {
+            synchronized(responseObserver) {
+                responseObserver.onError(t)
+            }
+        }
+
+        init {
+            LoggerInternal.setLogger(
+                object : CapabilityLogger {
+                    override fun log(
+                        logLevel: LogLevel,
+                        logTag: String,
+                        message: String,
+                    ) {
+                        when (logLevel) {
+                            LogLevel.ERROR -> Log.e(logTag, message)
+                            LogLevel.WARN -> Log.w(logTag, message)
+                            LogLevel.INFO -> Log.i(logTag, message)
+                        }
+                    }
+
+                    override fun log(
+                        logLevel: LogLevel,
+                        logTag: String,
+                        message: String,
+                        throwable: Throwable,
+                    ) {
+                        when (logLevel) {
+                            LogLevel.ERROR -> Log.e(logTag, message, throwable)
+                            LogLevel.WARN -> Log.w(logTag, message, throwable)
+                            LogLevel.INFO -> Log.i(logTag, message, throwable)
+                        }
+                    }
+                },
+            )
+        }
+    }
+
+    override fun startUpSession(
+        responseObserver: StreamObserver<StartSessionResponse>,
+    ): StreamObserver<StartSessionRequest> = StartSessionRequestObserver(responseObserver)
+
+    private inner class StartSessionRequestObserver internal constructor(
+        private val startSessionResponseObserver: StreamObserver<StartSessionResponse>,
+    ) : StreamObserver<StartSessionRequest> {
+
+        // Every AppInteractionService connection is defined by this streaming RPC connection.
+        // There should only be one session tied to each gRPC impl instance.
+        private var currentSessionId: String? = null
+
+        override fun onNext(request: StartSessionRequest) {
+            if (currentSessionId != null) {
+                return
+            }
+            val sessionId = request.getSessionIdentifier()!!
+            currentSessionId = sessionId
+            if (registeredCapabilities.size == 0) {
+                registeredCapabilities = appInteractionService.registeredCapabilities
+            }
+            val targetCapability = registeredCapabilities
+                .filter { request.getIdentifier() == it.id }
+                .firstOrNull()
+            if (targetCapability == null) {
+                return respondWithError(
+                    StatusRuntimeException(
+                        Status.FAILED_PRECONDITION.withDescription(
+                            ERROR_NO_ACTION_CAPABILITY,
+                        ),
+                    ),
+                    startSessionResponseObserver,
+                )
+            }
+            val hostProperties = HostProperties.Builder()
+                .setMaxHostSizeDp(
+                    SizeF(
+                        request.getHostProperties().getHostViewHeightDp(),
+                        request.getHostProperties().getHostViewWidthDp(),
+                    ),
+                ).build()
+            val session = targetCapability.createSession(
+                sessionId,
+                hostProperties,
+            )
+            SessionManager.putSession(sessionId, session)
+            startSessionResponseObserver.onNext(StartSessionResponse.getDefaultInstance())
+        }
+
+        override fun onError(t: Throwable) {
+            respondWithError(t, startSessionResponseObserver)
+            currentSessionId?.let(::destroySession)
+            currentSessionId = null
+        }
+
+        override fun onCompleted() {
+            synchronized(startSessionResponseObserver) {
+                startSessionResponseObserver.onCompleted()
+            }
+            currentSessionId?.let(::destroySession)
+            currentSessionId = null
+        }
+    }
+
+    override fun sendRequestFulfillment(
+        request: Request,
+        responseObserver: StreamObserver<Response>,
+    ) {
+        if (request.getFulfillmentRequest().getFulfillmentsList().isEmpty()) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(
+                        ERROR_NO_FULFILLMENT_REQUEST,
+                    ),
+                ),
+                responseObserver,
+            )
+        }
+        val selectedFulfillment = request.getFulfillmentRequest().getFulfillments(0)
+        val capability = registeredCapabilities
+            .filter { selectedFulfillment.getIdentifier() == it.id }
+            .firstOrNull()
+        if (capability == null) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(
+                        ERROR_NO_ACTION_CAPABILITY,
+                    ),
+                ),
+                responseObserver,
+            )
+        }
+        val sessionId = request.getSessionIdentifier()!!
+        val currentSession = SessionManager.getSession(sessionId)
+        if (currentSession == null) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION),
+                ),
+                responseObserver,
+            )
+        }
+        if (currentSession.status === CapabilitySession.Status.COMPLETED ||
+            currentSession.status === CapabilitySession.Status.DESTROYED
+        ) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED),
+                ),
+                responseObserver,
+            )
+        }
+        Futures.addCallback(
+            executeFulfillmentRequest(currentSession, selectedFulfillment),
+            object : FutureCallback<FulfillmentResponse> {
+                override fun onSuccess(fulfillmentResponse: FulfillmentResponse) {
+                    val responseBuilder =
+                        convertFulfillmentResponse(fulfillmentResponse, capability)
+                            .toBuilder()
+                    val uiCache = UiSessions.getUiCacheOrNull(sessionId)
+                    if (uiCache != null && uiCache.hasUnreadUiResponse()) {
+                        responseBuilder.setUiUpdate(UiUpdate.getDefaultInstance())
+                        if (!uiCache.getCachedChangedViewIds().isEmpty()) {
+                            responseBuilder.setCollectionUpdate(
+                                AppInteractionServiceProto.CollectionUpdate.newBuilder()
+                                    .addAllViewIds(uiCache.getCachedChangedViewIds()),
+                            )
+                        }
+                        uiCache.resetUnreadUiResponse()
+                    }
+                    respondAndComplete(responseBuilder.build(), responseObserver)
+                }
+
+                override fun onFailure(t: Throwable) {
+                    respondWithError(
+                        when {
+                            t is CapabilityExecutionException -> convertToGrpcException(t)
+                            t is StatusRuntimeException || t is StatusException -> t
+                            else -> StatusRuntimeException(
+                                Status.INTERNAL.withDescription(
+                                    t.message,
+                                ).withCause(t),
+                            )
+                        },
+                        responseObserver,
+                    )
+                    // Assistant will terminate the connection, which will reach
+                    // startUpSession.onError(t) / onCompleted()
+                }
+            },
+            Runnable::run,
+        )
+    }
+
+    override fun requestUi(
+        req: AppInteractionServiceProto.UiRequest,
+        responseObserver: StreamObserver<AppInteractionServiceProto.UiResponse>,
+    ) {
+        val sessionId = req.getSessionIdentifier()!!
+        val currentSession = SessionManager.getSession(sessionId)
+        if (currentSession == null) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION),
+                ),
+                responseObserver,
+            )
+        }
+        if (currentSession.status === CapabilitySession.Status.COMPLETED) {
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED),
+                ),
+                responseObserver,
+            )
+        }
+        val uiCache = UiSessions.getUiCacheOrNull(sessionId)
+        if (uiCache == null) {
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)),
+                responseObserver,
+            )
+        }
+        val tileLayout = uiCache.getCachedTileLayout()
+        val remoteViewsSize = uiCache.getCachedRemoteViewsSize()
+        val remoteViews = uiCache.getCachedRemoteViews()
+        if (tileLayout != null && remoteViews != null) {
+            // TODO(b/272379825): Decide if this is really an invalid state.
+            // both types of UI are present, this is a misused of API. We will treat it as error.
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.INTERNAL.withDescription(ERROR_MULTIPLE_UI_TYPES),
+                ),
+                responseObserver,
+            )
+        }
+        if (tileLayout != null) {
+            return respondAndComplete(
+                AppInteractionServiceProto.UiResponse.newBuilder()
+                    .setTileLayout(tileLayout.toProto())
+                    .build(),
+                responseObserver,
+            )
+        }
+        if (remoteViews != null && remoteViewsSize != null) {
+            RemoteViewsOverMetadataInterceptor.setRemoteViews(remoteViews)
+            return respondAndComplete(
+                AppInteractionServiceProto.UiResponse.newBuilder()
+                    .setRemoteViewsInfo(
+                        RemoteViewsInfo.newBuilder()
+                            .setWidthDp(remoteViewsSize.getWidth())
+                            .setHeightDp(remoteViewsSize.getHeight()),
+                    )
+                    .build(),
+                responseObserver,
+            )
+        }
+        destroySession(req.getSessionIdentifier())
+        respondWithError(
+            StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)),
+            responseObserver,
+        )
+    }
+
+    override fun requestCollection(
+        req: CollectionRequest,
+        responseObserver: StreamObserver<CollectionResponse>,
+    ) {
+        val sessionId = req.getSessionIdentifier()!!
+        val currentSession = SessionManager.getSession(sessionId)
+        if (currentSession == null) {
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_NO_SESSION),
+                ),
+                responseObserver,
+            )
+        }
+        if (currentSession.status === CapabilitySession.Status.COMPLETED) {
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.FAILED_PRECONDITION.withDescription(ERROR_SESSION_ENDED),
+                ),
+                responseObserver,
+            )
+        }
+        val uiCache = UiSessions.getUiCacheOrNull(sessionId)
+        if (uiCache == null) {
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(Status.INTERNAL.withDescription(ERROR_NO_UI)),
+                responseObserver,
+            )
+        }
+        val factory = uiCache.onGetViewFactoryInternal(req.getViewId())
+        if (factory == null) {
+            destroySession(req.getSessionIdentifier())
+            return respondWithError(
+                StatusRuntimeException(
+                    Status.UNIMPLEMENTED.withDescription(ERROR_NO_COLLECTION_SUPPORT),
+                ),
+                responseObserver,
+            )
+        }
+        when (req.getRequestDataCase()) {
+            RequestDataCase.ON_DESTROY -> {
+                requestCollectionOnDestroy(factory, responseObserver)
+            }
+            RequestDataCase.GET_COUNT -> {
+                requestCollectionGetCount(factory, responseObserver)
+            }
+            RequestDataCase.GET_VIEW_AT -> {
+                requestCollectionGetViewAt(
+                    factory,
+                    responseObserver,
+                    req.getGetViewAt().getPosition(),
+                )
+            }
+            RequestDataCase.GET_LOADING_VIEW -> {
+                requestCollectionGetLoadingView(factory, responseObserver)
+            }
+            RequestDataCase.GET_VIEW_TYPE_COUNT -> {
+                requestCollectionGetViewTypeCount(factory, responseObserver)
+            }
+            RequestDataCase.GET_ITEM_ID -> {
+                requestCollectionGetItemId(
+                    factory,
+                    responseObserver,
+                    req.getGetItemId().getPosition(),
+                )
+            }
+            RequestDataCase.HAS_STABLE_IDS -> {
+                requestCollectionHasStableIds(factory, responseObserver)
+            }
+            else -> {
+                // ignore it
+                Log.d(TAG, "received CollectionRequest with unknown RequestData case.")
+                responseObserver.onCompleted()
+            }
+        }
+    }
+
+    override fun requestGrounding(
+        request: GroundingRequest,
+        responseObserver: StreamObserver<GroundingResponse>,
+    ) {
+        val entityProvider = appInteractionService.registeredEntityProviders.filter {
+            it.id == request.getRequest().getEntityProviderId()
+        }.firstOrNull()
+        if (entityProvider == null) {
+            return respondAndComplete(
+                GroundingResponse.newBuilder()
+                    .setResponse(
+                        GroundingResponse.Response.newBuilder().setStatus(
+                            GroundingResponse.Status.INVALID_ENTITY_PROVIDER,
+                        ),
+                    ).build(),
+                responseObserver,
+            )
+        }
+        CoroutineScope(Dispatchers.Unconfined).launch {
+            try {
+                respondAndComplete(
+                    entityProvider.lookupInternal(request),
+                    responseObserver,
+                )
+            } catch (t: Throwable) {
+                respondWithError(
+                    when {
+                        t is StatusRuntimeException || t is StatusException -> t
+                        else -> StatusRuntimeException(
+                            Status.INTERNAL.withDescription(
+                                t.message,
+                            ).withCause(t),
+                        )
+                    },
+                    responseObserver,
+                )
+            }
+        }
+    }
+
+    private fun requestCollectionOnDestroy(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+    ) {
+        factory.onDestroy()
+        respondAndComplete(CollectionResponse.getDefaultInstance(), observer)
+    }
+
+    private fun requestCollectionGetCount(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+    ) {
+        respondAndComplete(
+            CollectionResponse.newBuilder()
+                .setGetCount(
+                    CollectionResponse.GetCount.newBuilder()
+                        .setCount(factory.getCount()),
+                )
+                .build(),
+            observer,
+        )
+    }
+
+    private fun requestCollectionGetViewAt(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+        position: Int,
+    ) {
+        factory.getViewAt(position)?.let(RemoteViewsOverMetadataInterceptor::setRemoteViews)
+        respondAndComplete(CollectionResponse.getDefaultInstance(), observer)
+    }
+
+    private fun requestCollectionGetLoadingView(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+    ) {
+        factory.getLoadingView()?.let(RemoteViewsOverMetadataInterceptor::setRemoteViews)
+        respondAndComplete(CollectionResponse.getDefaultInstance(), observer)
+    }
+
+    private fun requestCollectionGetViewTypeCount(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+    ) {
+        respondAndComplete(
+            CollectionResponse.newBuilder()
+                .setGetViewTypeCount(
+                    CollectionResponse.GetViewTypeCount.newBuilder()
+                        .setViewTypeCount(factory.getViewTypeCount()),
+                )
+                .build(),
+            observer,
+        )
+    }
+
+    private fun requestCollectionGetItemId(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+        position: Int,
+    ) {
+        respondAndComplete(
+            CollectionResponse.newBuilder()
+                .setGetItemId(
+                    CollectionResponse.GetItemId.newBuilder()
+                        .setItemId(factory.getItemId(position)),
+                )
+                .build(),
+            observer,
+        )
+    }
+
+    private fun requestCollectionHasStableIds(
+        factory: RemoteViewsFactory,
+        observer: StreamObserver<CollectionResponse>,
+    ) {
+        respondAndComplete(
+            CollectionResponse.newBuilder()
+                .setHasStableIds(
+                    CollectionResponse.HasStableIds.newBuilder()
+                        .setHasStableIds(factory.hasStableIds()),
+                )
+                .build(),
+            observer,
+        )
+    }
+
+    private fun convertToAppActionsContextVersion(
+        libInfoVersion: LibInfo.Version,
+    ): Version {
+        val builder = Version.newBuilder()
+            .setMajor(libInfoVersion.major.toLong())
+            .setMinor(libInfoVersion.minor.toLong())
+            .setPatch(libInfoVersion.patch.toLong())
+        libInfoVersion.preReleaseId?.let(builder::setPrereleaseId)
+        return builder.build()
+    }
+
+    private fun destroySession(sessionId: String) {
+        SessionManager.getSession(sessionId)?.destroy()
+        SessionManager.removeSession(sessionId)
+    }
+
+    internal fun convertToGrpcException(e: CapabilityExecutionException): StatusRuntimeException {
+        return when (e.getErrorStatus()) {
+            ErrorStatusInternal.TIMEOUT -> StatusRuntimeException(
+                Status.DEADLINE_EXCEEDED.withDescription(e.message).withCause(e),
+            )
+            else -> StatusRuntimeException(
+                Status.INTERNAL.withDescription(e.message).withCause(e),
+            )
+        }
+    }
+
+    internal fun convertFulfillmentResponse(
+        fulfillmentResponse: FulfillmentResponse,
+        capability: Capability,
+    ): Response {
+        val appAction = capability.appAction
+        val isDialogSession = appAction.getTaskInfo().getSupportsPartialFulfillment()
+        val version = convertToAppActionsContextVersion(
+            LibInfo(appInteractionService.getApplicationContext()).getVersion(),
+        )
+        val responseBuilder: Response.Builder =
+            // TODO(b/269638788): Add DialogState to the Response proto.
+            Response.newBuilder()
+                .setFulfillmentResponse(fulfillmentResponse)
+                .setAppActionsContext(
+                    AppActionsContext.newBuilder()
+                        .addActions(appAction)
+                        .setVersion(version)
+                        .build(),
+                )
+        if (!isDialogSession) {
+            responseBuilder.setEndingStatus(
+                AppInteractionServiceProto.Status.newBuilder()
+                    .setStatusCode(Code.COMPLETE)
+                    .build(),
+            )
+        }
+        return responseBuilder.build()
+    }
+
+    private fun executeFulfillmentRequest(
+        session: CapabilitySession,
+        fulfillmentRequest: FulfillmentRequest.Fulfillment,
+    ): ListenableFuture<FulfillmentResponse> = CallbackToFutureAdapter.getFuture { completer ->
+        session.execute(
+            ArgumentsWrapper.create(fulfillmentRequest),
+            CapabilityCallback(completer),
+        )
+        "executing action capability"
+    }
+}
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiCache.java b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiCache.java
index 5683769..e0f8353 100644
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiCache.java
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiCache.java
@@ -22,7 +22,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.BaseSession;
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -65,7 +65,7 @@
     private boolean mUnreadUiResponse;
 
     /**
-     * Caches a UiResponse for this particular {@link BaseSession}.
+     * Caches a UiResponse for this particular {@link BaseExecutionSession}.
      */
     public void updateUiInternal(@NonNull UiResponse uiResponse) {
         synchronized (mLock) {
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 7621aee..2366e49 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
@@ -19,7 +19,7 @@
 
 import androidx.annotation.GuardedBy
 import androidx.appactions.interaction.capabilities.core.ActionExecutor
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.impl.UiHandleRegistry
 import androidx.appactions.interaction.service.UiSessions.removeUiCache
 import javax.annotation.concurrent.ThreadSafe
@@ -64,8 +64,8 @@
     }
 }
 
-/** Return a UI associated with this [BaseSession]. */
-fun BaseSession<*, *>.updateUi(uiResponse: UiResponse) =
+/** Return a UI associated with this [BaseExecutionSession]. */
+fun BaseExecutionSession<*, *>.updateUi(uiResponse: UiResponse) =
     UiSessions.getOrCreateUiCache(
         UiHandleRegistry.getSessionIdFromUiHandle(this)!!
     ).updateUiInternal(uiResponse)
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceEntityProviderTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceEntityProviderTest.kt
new file mode 100644
index 0000000..6f339d4
--- /dev/null
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceEntityProviderTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.service
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.values.Alarm
+import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils.buildSearchActionParamValue
+import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.awaitSync
+import androidx.appactions.interaction.proto.GroundingRequest
+import androidx.appactions.interaction.proto.GroundingRequest.Request
+import androidx.appactions.interaction.proto.GroundingResponse
+import androidx.appactions.interaction.proto.GroundingResponse.Candidate
+import androidx.appactions.interaction.proto.GroundingResponse.Response
+import androidx.appactions.interaction.proto.GroundingResponse.Status
+import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc
+import androidx.appactions.interaction.service.testing.internal.FakeAlarmEntityProvider
+import androidx.appactions.interaction.service.testing.internal.FakeAppInteractionService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import io.grpc.BindableService
+import io.grpc.ManagedChannel
+import io.grpc.Server
+import io.grpc.inprocess.InProcessChannelBuilder
+import io.grpc.inprocess.InProcessServerBuilder
+import io.grpc.stub.StreamObserver
+import io.grpc.testing.GrpcCleanupRule
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.times
+import org.robolectric.Robolectric
+
+// TODO(b/271929200) Implement tests for the 2 UI related RPCs
+@RunWith(AndroidJUnit4::class)
+class AppInteractionServiceEntityProviderTest {
+    val testServerName = "testServer"
+
+    @get:Rule val grpcCleanup = GrpcCleanupRule()
+
+    private lateinit var appInteractionService: FakeAppInteractionService
+
+    @Before
+    fun before() {
+        appInteractionService = Robolectric.buildService(
+            FakeAppInteractionService::class.java
+        ).get()
+    }
+
+    @Test
+    fun alarmProvider_incorrectProviderId(): Unit = runBlocking {
+        val morningAlarm = Alarm.newBuilder().setId("alarm1").setName("Morning Alarm").build()
+        val alarmProvider = FakeAlarmEntityProvider(
+            "alarmProvider",
+            listOf(morningAlarm)
+        )
+        appInteractionService.registeredEntityProviders = listOf(alarmProvider)
+        val server =
+            createInProcessServer(
+                AppInteractionServiceGrpcImpl(appInteractionService)
+            )
+
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+
+        val groundingRequest = GroundingRequest
+            .newBuilder()
+            .setRequest(
+                Request.newBuilder()
+                    .setEntityProviderId("randomIncorrectId")
+                    .setSearchAction(
+                        buildSearchActionParamValue(
+                            "alarm search query"
+                        )
+                    )
+            )
+            .build()
+
+        val responseObserver = TestStreamObserver<GroundingResponse>()
+        stub.requestGrounding(groundingRequest, responseObserver)
+        val groundingResponse = responseObserver.firstResultDeferred.awaitSync()
+        assertThat(groundingResponse).isEqualTo(
+            GroundingResponse.newBuilder()
+            .setResponse(
+                GroundingResponse.Response.newBuilder().setStatus(
+                    GroundingResponse.Status.INVALID_ENTITY_PROVIDER,
+                ),
+            ).build()
+        )
+
+        server.shutdownNow()
+    }
+
+    @Test
+    fun alarmProvider_success(): Unit = runBlocking {
+        val morningAlarm = Alarm.newBuilder().setId("alarm1").setName("Morning Alarm").build()
+        val alarmProvider = FakeAlarmEntityProvider(
+            "alarmProvider",
+            listOf(morningAlarm)
+        )
+        appInteractionService.registeredEntityProviders = listOf(alarmProvider)
+        val server =
+            createInProcessServer(
+                AppInteractionServiceGrpcImpl(appInteractionService)
+            )
+
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+
+        val groundingRequest = GroundingRequest
+            .newBuilder()
+            .setRequest(
+                Request.newBuilder()
+                    .setEntityProviderId("alarmProvider")
+                    .setSearchAction(
+                        buildSearchActionParamValue(
+                            "alarm search query"
+                        )
+                    )
+            )
+            .build()
+
+        val responseObserver = TestStreamObserver<GroundingResponse>()
+        stub.requestGrounding(groundingRequest, responseObserver)
+        val groundingResponse = responseObserver.firstResultDeferred.awaitSync()
+        assertThat(groundingResponse).isEqualTo(
+            GroundingResponse.newBuilder().setResponse(
+                Response.newBuilder()
+                    .setStatus(Status.SUCCESS)
+                    .addCandidates(
+                        Candidate.newBuilder().setGroundedEntity(
+                            EntityConverter.of(TypeConverters.ALARM_TYPE_SPEC).convert(morningAlarm)
+                        )
+                    )
+            ).build()
+        )
+
+        server.shutdownNow()
+    }
+
+    private fun createInProcessServer(
+        service: BindableService
+    ): Server {
+        return grpcCleanup.register(
+            InProcessServerBuilder.forName(testServerName)
+                .directExecutor()
+                .addService(service)
+                .build()
+                .start()
+        )
+    }
+
+    private fun createInProcessChannel(): ManagedChannel {
+        return grpcCleanup.register(
+            InProcessChannelBuilder.forName(testServerName).directExecutor().build()
+        )
+    }
+
+    /** Captures the first error or value received by the stream observer */
+    private class TestStreamObserver<T> : StreamObserver<T> {
+        val firstResultDeferred = CompletableDeferred<T>()
+
+        override fun onNext(value: T) {
+            firstResultDeferred.complete(value)
+        }
+
+        override fun onError(t: Throwable) {
+            firstResultDeferred.completeExceptionally(t)
+        }
+
+        override fun onCompleted() {
+            firstResultDeferred.cancel()
+        }
+    }
+}
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
index b466a6e..e9b80f9 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.appactions.interaction.service
 
-import android.content.Context
 import androidx.appactions.interaction.capabilities.core.Capability
 import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
@@ -27,17 +26,17 @@
 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.service.AppInteractionServiceGrpcImpl.ERROR_NO_ACTION_CAPABILITY
-import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.ERROR_NO_FULFILLMENT_REQUEST
-import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.ERROR_NO_SESSION
-import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.ERROR_SESSION_ENDED
+import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.Companion.ERROR_NO_ACTION_CAPABILITY
+import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.Companion.ERROR_NO_FULFILLMENT_REQUEST
+import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.Companion.ERROR_NO_SESSION
+import androidx.appactions.interaction.service.AppInteractionServiceGrpcImpl.Companion.ERROR_SESSION_ENDED
+import androidx.appactions.interaction.service.testing.internal.FakeAppInteractionService
 import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc
 import androidx.appactions.interaction.service.proto.AppInteractionServiceGrpc.AppInteractionServiceStub
 import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.Request
 import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionRequest
 import androidx.appactions.interaction.service.proto.AppInteractionServiceProto.StartSessionResponse
 import androidx.concurrent.futures.await
-import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import org.mockito.kotlin.any
@@ -48,6 +47,7 @@
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
+import org.robolectric.Robolectric
 import io.grpc.BindableService
 import io.grpc.ManagedChannel
 import io.grpc.Server
@@ -55,14 +55,11 @@
 import io.grpc.ServerInterceptors
 import io.grpc.Status
 import io.grpc.StatusRuntimeException
-import io.grpc.binder.SecurityPolicies
-import io.grpc.binder.SecurityPolicy
 import io.grpc.inprocess.InProcessChannelBuilder
 import io.grpc.inprocess.InProcessServerBuilder
 import io.grpc.stub.StreamObserver
 import io.grpc.testing.GrpcCleanupRule
 import java.io.IOException
-import java.util.Collections
 import kotlin.test.assertFailsWith
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.runBlocking
@@ -77,7 +74,6 @@
 
     @get:Rule val grpcCleanup = GrpcCleanupRule()
 
-    private val context = ApplicationProvider.getApplicationContext<Context>()
     private val remoteViewsInterceptor: ServerInterceptor = RemoteViewsOverMetadataInterceptor()
     private val testServerName = InProcessServerBuilder.generateName()
     private val testBiiName = "actions.intent.SAMPLE_BII_NAME"
@@ -102,22 +98,27 @@
                     .addOutputValues(OutputValue.newBuilder().setName("bio_arg1")),
             )
             .build()
-    private var capability1 = mock<Capability>()
+    private lateinit var capability1: Capability
+    private lateinit var appInteractionService: FakeAppInteractionService
 
     @Before
     fun before() {
         capability1 = mock()
         whenever(capability1.id).thenReturn(capabilityId)
-        whenever(capability1.getAppAction()).thenReturn(AppAction.getDefaultInstance())
+        whenever(capability1.appAction).thenReturn(AppAction.getDefaultInstance())
         val mockCapabilitySession = createMockSession()
         whenever(capability1.createSession(any(), any())).thenReturn(mockCapabilitySession)
+        appInteractionService = Robolectric.buildService(
+            FakeAppInteractionService::class.java
+        ).get()
+        appInteractionService.registeredCapabilities = listOf(capability1)
     }
 
     @Test
     fun startUpSession_validRequest_shouldGetValidStartSessionResponse(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -152,7 +153,7 @@
     fun startUpSession_shouldFailWhenNoStaticCapability(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -186,7 +187,7 @@
     fun sendRequestFulfillment_shouldGetValidResponse(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
         val channel = createInProcessChannel()
@@ -213,7 +214,7 @@
     fun sendRequestFulfillment_shouldFailWhenNoFulfillment(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -243,7 +244,7 @@
     fun sendRequestFulfillment_shouldFailWhenNoStaticCapability(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -278,7 +279,7 @@
     fun sendRequestFulfillment_shouldFailWhenNoSession(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -310,7 +311,7 @@
     fun sendRequestFulfillment_shouldFailWhenSessionEnded(): Unit = runBlocking {
         val server =
             createInProcessServer(
-                AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
+                AppInteractionServiceGrpcImpl(appInteractionService),
                 remoteViewsInterceptor,
             )
 
@@ -395,18 +396,4 @@
         whenever(mockSession.uiHandle).thenReturn(Any())
         return mockSession
     }
-
-    private inner class FakeAppInteractionService(capabilities: List<Capability>) :
-        AppInteractionService() {
-        override val registeredCapabilities: MutableList<Capability> =
-            capabilities.toMutableList()
-
-        override val securityPolicy: SecurityPolicy = SecurityPolicies.internalOnly()
-
-        override val allowedApps: List<AppVerificationInfo> = Collections.emptyList()
-
-        override fun getApplicationContext(): Context {
-            return context
-        }
-    }
 }
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiCacheTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiCacheTest.kt
index 4cf3bbe..9d9056c 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiCacheTest.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiCacheTest.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import android.util.SizeF
 import android.widget.RemoteViews
-import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.service.test.R
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,7 +29,7 @@
 
 @RunWith(AndroidJUnit4::class)
 class UiCacheTest {
-    private val capabilitySession = object : BaseSession<String, String> {}
+    private val capabilitySession = object : BaseExecutionSession<String, String> {}
     private val context: Context = ApplicationProvider.getApplicationContext()
     private val remoteViewsFactoryId = 123
     private val changeViewId = 111
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 6a2737c..16ec377 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
@@ -22,7 +22,7 @@
 import androidx.appactions.interaction.capabilities.core.ActionExecutor
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.HostProperties
-import androidx.appactions.interaction.capabilities.core.SessionFactory
+import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
 import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.Type.SYNC
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.service.test.R
@@ -33,7 +33,7 @@
 import androidx.appactions.interaction.service.testing.internal.FakeCapability
 import androidx.appactions.interaction.service.testing.internal.FakeCapability.Arguments
 import androidx.appactions.interaction.service.testing.internal.FakeCapability.Output
-import androidx.appactions.interaction.service.testing.internal.FakeCapability.Session
+import androidx.appactions.interaction.service.testing.internal.FakeCapability.ExecutionSession
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.wear.tiles.LayoutElementBuilders
@@ -45,16 +45,16 @@
 
 @RunWith(AndroidJUnit4::class)
 class UiSessionsTest {
-    private val sessionFactory = object : SessionFactory<Session> {
-        private val sessions = mutableListOf<Session>()
+    private val sessionFactory = object : ExecutionSessionFactory<ExecutionSession> {
+        private val sessions = mutableListOf<ExecutionSession>()
         private var index = 0
         override fun createSession(
             hostProperties: HostProperties?,
-        ): Session {
+        ): ExecutionSession {
             return sessions[index++]
         }
 
-        fun addSessions(vararg session: Session) {
+        fun addExecutionSessions(vararg session: ExecutionSession) {
             sessions.addAll(session)
         }
 
@@ -68,7 +68,7 @@
         HostProperties.Builder().setMaxHostSizeDp(SizeF(300f, 500f)).build()
     private val multiTurnCapability = FakeCapability.CapabilityBuilder()
         .setId("multiTurnCapability")
-        .setSessionFactory(sessionFactory).build()
+        .setExecutionSessionFactory(sessionFactory).build()
 
     private val context: Context = ApplicationProvider.getApplicationContext()
     private val remoteViewsFactoryId = 123
@@ -104,9 +104,9 @@
         UiSessions.removeUiCache(sessionId)
     }
 
-    fun createFakeSessionWithUiResponses(vararg uiResponses: UiResponse): Session {
-        return object : Session {
-            override suspend fun onFinish(
+    fun createFakeSessionWithUiResponses(vararg uiResponses: UiResponse): ExecutionSession {
+        return object : ExecutionSession {
+            override suspend fun onExecute(
                 arguments: Arguments,
             ): ExecutionResult<Output> {
                 for (uiResponse in uiResponses) {
@@ -121,7 +121,7 @@
     fun sessionExtensionMethod_createCache_removeCache() {
         assertThat(UiSessions.getUiCacheOrNull(sessionId)).isNull()
 
-        sessionFactory.addSessions(
+        sessionFactory.addExecutionSessions(
             createFakeSessionWithUiResponses(remoteViewsUiResponse),
         )
         val session = multiTurnCapability.createSession(sessionId, hostProperties)
@@ -150,8 +150,8 @@
     @Test
     fun multipleUpdate_sharesCache() {
         assertThat(UiSessions.getUiCacheOrNull(sessionId)).isNull()
-        sessionFactory.addSessions(object : Session {
-            override suspend fun onFinish(
+        sessionFactory.addExecutionSessions(object : ExecutionSession {
+            override suspend fun onExecute(
                 arguments: Arguments,
             ): ExecutionResult<Output> {
                 this.updateUi(remoteViewsUiResponse)
@@ -184,17 +184,17 @@
     fun multipleSession_haveTheirOwnCache() {
         val sessionId1 = "fakeSessionId1"
         val sessionId2 = "fakeSessionId2"
-        sessionFactory.addSessions(
-            object : Session {
-                override suspend fun onFinish(
+        sessionFactory.addExecutionSessions(
+            object : ExecutionSession {
+                override suspend fun onExecute(
                     arguments: Arguments,
                 ): ExecutionResult<Output> {
                     this.updateUi(remoteViewsUiResponse)
                     return ExecutionResult.Builder<Output>().build()
                 }
             },
-            object : Session {
-                override suspend fun onFinish(
+            object : ExecutionSession {
+                override suspend fun onExecute(
                     arguments: Arguments,
                 ): ExecutionResult<Output> {
                     this.updateUi(tileLayoutUiResponse)
@@ -254,7 +254,7 @@
         val oneShotCapability = FakeCapability.CapabilityBuilder().setId(
             "oneShotCapability",
         ).setExecutor(object : ActionExecutor<Arguments, Output> {
-            override suspend fun execute(arguments: Arguments): ExecutionResult<Output> {
+            override suspend fun onExecute(arguments: Arguments): ExecutionResult<Output> {
                 this.updateUi(remoteViewsUiResponse)
                 return ExecutionResult.Builder<Output>().build()
             }
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAlarmEntityProvider.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAlarmEntityProvider.kt
new file mode 100644
index 0000000..c2b23b1
--- /dev/null
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAlarmEntityProvider.kt
@@ -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.
+ */
+
+package androidx.appactions.interaction.service.testing.internal
+
+import androidx.appactions.interaction.capabilities.core.entity.EntityProvider
+import androidx.appactions.interaction.capabilities.core.entity.EntityLookupRequest
+import androidx.appactions.interaction.capabilities.core.entity.EntityLookupResponse
+import androidx.appactions.interaction.capabilities.core.entity.EntityLookupCandidate
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.values.Alarm
+
+class FakeAlarmEntityProvider(
+    override val id: String,
+    private val alarms: List<Alarm> = listOf()
+) : EntityProvider<Alarm>(
+    TypeConverters.ALARM_TYPE_SPEC
+) {
+    override suspend fun lookup(
+        request: EntityLookupRequest<Alarm>
+    ): EntityLookupResponse<Alarm> {
+        return EntityLookupResponse.Builder<Alarm>().setCandidateList(
+            alarms.map { EntityLookupCandidate.Builder<Alarm>().setCandidate(it).build() }
+        ).setStatus(
+            EntityLookupResponse.SUCCESS
+        ).build()
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAppInteractionService.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAppInteractionService.kt
new file mode 100644
index 0000000..9521309
--- /dev/null
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeAppInteractionService.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.service.testing.internal
+
+import androidx.appactions.interaction.service.AppInteractionService
+import androidx.appactions.interaction.service.AppVerificationInfo
+import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.entity.EntityProvider
+import io.grpc.binder.SecurityPolicies
+import io.grpc.binder.SecurityPolicy
+
+class FakeAppInteractionService() : AppInteractionService() {
+    override var registeredCapabilities: List<Capability> = listOf()
+    override var registeredEntityProviders: List<EntityProvider<*>> = listOf()
+    override val securityPolicy: SecurityPolicy = SecurityPolicies.internalOnly()
+    override val allowedApps: List<AppVerificationInfo> = listOf()
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
index 609f921..fce9017 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
@@ -17,22 +17,21 @@
 package androidx.appactions.interaction.service.testing.internal
 
 import androidx.appactions.interaction.capabilities.core.Capability
-import androidx.appactions.interaction.capabilities.core.BaseSession
-import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
-import androidx.appactions.interaction.capabilities.core.SessionFactory
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
 import androidx.appactions.interaction.capabilities.core.ValueListener
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.ParamProperty
+import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
 import androidx.appactions.interaction.capabilities.core.impl.task.TaskHandler
 import java.util.Optional
 
 private const val CAPABILITY_NAME = "actions.intent.FAKE_CAPABILITY"
 private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(FakeCapability.Property::class.java)
+    .setDescriptor(FakeCapability.Properties::class.java)
     .setArguments(FakeCapability.Arguments::class.java, FakeCapability.Arguments::Builder)
     .setOutput(FakeCapability.Output::class.java).bindOptionalParameter(
         "fieldOne",
@@ -43,8 +42,8 @@
     ).build()
 
 class FakeCapability private constructor() {
-    class Property(
-        val fieldOne: ParamProperty<StringValue>? = null,
+    class Properties(
+        val fieldOne: Property<StringValue>? = null,
     )
 
     class Arguments internal constructor(
@@ -63,20 +62,20 @@
 
     class Confirmation internal constructor()
 
-    interface Session : BaseSession<Arguments, Output> {
+    interface ExecutionSession : BaseExecutionSession<Arguments, Output> {
         val fieldOneListener: ValueListener<String>?
             get() = null
     }
 
-    class CapabilityBuilder : CapabilityBuilderBase<
+    class CapabilityBuilder : Capability.Builder<
         CapabilityBuilder,
-        Property,
+        Properties,
         Arguments,
         Output,
         Confirmation,
-        Session,
+        ExecutionSession,
         >(ACTION_SPEC) {
-        override val sessionBridge = SessionBridge<Session, Confirmation> {
+        override val sessionBridge = SessionBridge<ExecutionSession, Confirmation> {
                 session ->
             val builder = TaskHandler.Builder<Confirmation>()
             session.fieldOneListener?.let {
@@ -89,18 +88,18 @@
             builder.build()
         }
 
-        private var fieldOne: ParamProperty<StringValue>? = null
+        private var fieldOne: Property<StringValue>? = null
 
-        fun setFieldOne(fieldOne: ParamProperty<StringValue>) = apply {
+        fun setFieldOne(fieldOne: Property<StringValue>) = apply {
             this.fieldOne = fieldOne
         }
 
-        public override fun setSessionFactory(
-            sessionFactory: SessionFactory<Session>,
-        ) = super.setSessionFactory(sessionFactory)
+        public override fun setExecutionSessionFactory(
+            sessionFactory: ExecutionSessionFactory<ExecutionSession>,
+        ) = super.setExecutionSessionFactory(sessionFactory)
 
         override fun build(): Capability {
-            super.setProperty(Property(fieldOne))
+            super.setProperty(Properties(fieldOne))
             return super.build()
         }
     }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
index 055ccfd..6398e36 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
@@ -19,10 +19,13 @@
 import android.os.Build
 import androidx.benchmark.junit4.PerfettoTraceRule
 import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
+import androidx.core.os.BuildCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import kotlin.test.assertNull
+import org.junit.Assume
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,6 +37,12 @@
     @get:Rule
     val perfettoTraceRule = PerfettoTraceRule()
 
+    @Before
+    fun setUp() {
+        // TODO: to re-enable for api 34 (b/276970167)
+        Assume.assumeTrue(!BuildCompat.isAtLeastU())
+    }
+
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
     @Test
     fun installProfile() {
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 e41a947..d839e8f 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
@@ -27,6 +27,7 @@
 import androidx.benchmark.Outputs
 import androidx.benchmark.Shell
 import androidx.benchmark.userspaceTrace
+import androidx.core.os.BuildCompat
 import java.io.File
 
 /**
@@ -147,7 +148,8 @@
                     // Don't reset for subsequent iterations
                     Log.d(TAG, "Killing package $packageName")
                     killProcessBlock()
-                    mode.compileImpl(packageName = packageName,
+                    mode.compileImpl(
+                        packageName = packageName,
                         killProcessBlock = killProcessBlock
                     ) {
                         scope.iteration = iteration
@@ -172,7 +174,8 @@
                 lastProfile = unfilteredProfile
                 stableCount = 1
             } else {
-                Log.d(TAG,
+                Log.d(
+                    TAG,
                     "Profiles stable in iteration $iteration (for $stableCount iterations)"
                 )
                 stableCount += 1
@@ -295,10 +298,22 @@
  * Does not require root.
  */
 @RequiresApi(33)
[email protected](BuildCompat.PrereleaseSdkCheck::class)
 private fun extractProfile(packageName: String): String {
-    Shell.executeScriptSilent(
-        "pm dump-profiles --dump-classes-and-methods $packageName"
-    )
+
+    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 fileName = "$packageName-primary.prof.txt"
     Shell.executeScriptSilent(
         "mv /data/misc/profman/$fileName ${Outputs.dirUsableByAppAndShell}/"
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 6b0459e..81aba53 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
@@ -39,6 +39,7 @@
 internal class GattClientImpl {
     companion object {
         private const val TAG = "GattClientImpl"
+        private const val GATT_MAX_MTU = 517
     }
     private data class ClientTask(
         val taskBlock: () -> Unit
@@ -75,11 +76,21 @@
         val callback = object : BluetoothGattCallback() {
             override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                 if (newState == BluetoothGatt.STATE_CONNECTED) {
+                    gatt?.requestMtu(GATT_MAX_MTU)
+                } else {
+                    connectResult.complete(false)
+                    // TODO(b/270492198): throw precise exception
+                    finished.completeExceptionally(IllegalStateException("connect failed"))
+                }
+            }
+
+            override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
+                if (status == GATT_SUCCESS) {
                     gatt?.discoverServices()
                 } else {
                     connectResult.complete(false)
-                    // TODO: throw precise exception
-                    finished.completeExceptionally(IllegalStateException("??"))
+                    // TODO(b/270492198): throw precise exception
+                    finished.completeExceptionally(IllegalStateException("mtu request failed"))
                 }
             }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index f3b5a89..1bea081 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -19,7 +19,6 @@
 import androidx.benchmark.gradle.BenchmarkPlugin
 import androidx.build.AndroidXImplPlugin.Companion.TASK_TIMEOUT_MINUTES
 import androidx.build.Release.DEFAULT_PUBLISH_CONFIG
-import androidx.build.SupportConfig.BUILD_TOOLS_VERSION
 import androidx.build.SupportConfig.COMPILE_SDK_VERSION
 import androidx.build.SupportConfig.DEFAULT_MIN_SDK_VERSION
 import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER
@@ -591,7 +590,7 @@
         }
 
         compileSdkVersion(COMPILE_SDK_VERSION)
-        buildToolsVersion = BUILD_TOOLS_VERSION
+        buildToolsVersion = SupportConfig.buildToolsVersion(project)
         defaultConfig.targetSdk = TARGET_SDK_VERSION
         ndkVersion = SupportConfig.NDK_VERSION
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index cab8d67..25452de 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -98,7 +98,7 @@
                 is LibraryPlugin -> {
                     val libraryExtension = project.extensions.getByType<LibraryExtension>()
                     libraryExtension.compileSdkVersion = SupportConfig.COMPILE_SDK_VERSION
-                    libraryExtension.buildToolsVersion = SupportConfig.BUILD_TOOLS_VERSION
+                    libraryExtension.buildToolsVersion = SupportConfig.buildToolsVersion(project)
 
                     // Use a local debug keystore to avoid build server issues.
                     val debugSigningConfig = libraryExtension.signingConfigs.getByName("debug")
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt b/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
index 439f4c9..35a8575 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
@@ -28,6 +28,7 @@
 import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.api.provider.Property
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.OutputDirectory
@@ -57,7 +58,7 @@
     val compileSdkVersion: String = SupportConfig.COMPILE_SDK_VERSION
 
     @get:Input
-    val buildToolsVersion: String = SupportConfig.BUILD_TOOLS_VERSION
+    abstract val buildToolsVersion: Property<String>
 
     @get:Input
     val minSdkVersion: Int = SupportConfig.DEFAULT_MIN_SDK_VERSION
@@ -109,7 +110,7 @@
             writer.write("navigationRuntime=$navigationRuntime\n")
             writer.write("kotlinStdlib=$kotlinStdlib\n")
             writer.write("compileSdkVersion=$compileSdkVersion\n")
-            writer.write("buildToolsVersion=$buildToolsVersion\n")
+            writer.write("buildToolsVersion=${buildToolsVersion.get()}\n")
             writer.write("minSdkVersion=$minSdkVersion\n")
             writer.write("kotlinVersion=$kotlinVersion\n")
             writer.write("kspVersion=$kspVersion\n")
@@ -136,6 +137,11 @@
                     project.getRepositoryDirectory().toRelativeString(project.projectDir)
                 it.debugKeystore.set(project.getKeystore())
                 it.outputDir.set(generatedDirectory)
+                it.buildToolsVersion.set(
+                    project.provider {
+                        SupportConfig.buildToolsVersion(project)
+                    }
+                )
                 it.buildSrcOutRelativePath =
                     (project.properties["buildSrcOut"] as File).toRelativeString(project.projectDir)
                 // Copy repositories used for the library project so that it can replicate the same
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
index 9842cf0..de3d495 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -24,7 +24,8 @@
 object SupportConfig {
     const val DEFAULT_MIN_SDK_VERSION = 14
     const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
-    const val BUILD_TOOLS_VERSION = "34.0.0-rc3"
+    private const val INTERNAL_BUILD_TOOLS_VERSION = "34.0.0-rc3"
+    private const val PUBLIC_BUILD_TOOLS_VERSION = "33.0.1"
     const val NDK_VERSION = "23.1.7779620"
 
     /**
@@ -47,6 +48,20 @@
      * set to a pre-release version, tests will only be able to run on pre-release devices.
      */
     const val TARGET_SDK_VERSION = 33
+
+    /**
+     * Returns the build tools version that should be used for the project.
+     *
+     * Note that the value might be different between the internal and external (github) builds.
+     */
+    @JvmStatic
+    fun buildToolsVersion(project: Project): String {
+        return if (ProjectLayoutType.isPlayground(project)) {
+            PUBLIC_BUILD_TOOLS_VERSION
+        } else {
+            INTERNAL_BUILD_TOOLS_VERSION
+        }
+    }
 }
 
 fun Project.getExternalProjectPath(): File {
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
index e527c4b..9a81718 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
@@ -192,13 +192,16 @@
     }
 
     private fun skipTestOnDevicesWithProblematicBuild() {
-        // Skip test for b/265613005 and b/223439995
+        // Skip test for b/265613005, b/223439995 and b/277174217
         val hasVideoProfilesQuirk = DeviceQuirks[InvalidVideoProfilesQuirk::class.java] != null
-        val isProblematicCuttlefishBuild =
-            Build.MODEL.contains("Cuttlefish") && Build.ID.startsWith("TP1A")
         Assume.assumeFalse(
             "Skip test with null VideoProfile issue. Unable to test.",
-            hasVideoProfilesQuirk || isProblematicCuttlefishBuild
+            hasVideoProfilesQuirk || isProblematicCuttlefishBuild()
         )
     }
+
+    private fun isProblematicCuttlefishBuild(): Boolean {
+        return Build.MODEL.contains("Cuttlefish", true) &&
+            (Build.ID.startsWith("TP1A", true) || Build.ID.startsWith("TSE4", true))
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
index 70feb5d..5305012 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
@@ -51,7 +51,7 @@
     private val surfaceUsageMap: MutableMap<Surface, AutoCloseable> = mutableMapOf()
 
     @GuardedBy("lock")
-    private val closed: Boolean = false
+    private var closed: Boolean = false
 
     operator fun set(streamId: StreamId, surface: Surface?) {
         val closeable =
@@ -104,6 +104,7 @@
                 if (closed) {
                     return
                 }
+                closed = true
                 surfaceMap.clear()
                 val tokensToClose = surfaceUsageMap.values.toList()
                 surfaceUsageMap.clear()
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
index 4376357..81c3054 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
@@ -187,13 +187,16 @@
     }
 
     private fun skipTestOnDevicesWithProblematicBuild() {
-        // Skip test for b/265613005 and b/223439995
+        // Skip test for b/265613005, b/223439995 and b/277174217
         val hasVideoProfilesQuirk = DeviceQuirks.get(InvalidVideoProfilesQuirk::class.java) != null
-        val isProblematicCuttlefishBuild =
-            Build.MODEL.contains("Cuttlefish") && Build.ID.startsWith("TP1A")
         assumeFalse(
             "Skip test with null VideoProfile issue. Unable to test.",
-            hasVideoProfilesQuirk || isProblematicCuttlefishBuild
+            hasVideoProfilesQuirk || isProblematicCuttlefishBuild()
         )
     }
+
+    private fun isProblematicCuttlefishBuild(): Boolean {
+        return Build.MODEL.contains("Cuttlefish", true) &&
+            (Build.ID.startsWith("TP1A", true) || Build.ID.startsWith("TSE4", true))
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.kt
index badc058..2e24387 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.kt
@@ -74,10 +74,12 @@
     }
 
     @Test
-    fun expectedFrameRateIsNull_whenNotSet() {
+    fun expectedFrameRateIsUnspecified_whenNotSet() {
         val resolution = Size(640, 480)
         val request = createNewRequest(resolution)
-        Truth.assertThat(request.expectedFrameRate).isNull()
+        Truth.assertThat(request.expectedFrameRate).isEqualTo(
+            SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
+        )
     }
 
     @Test
@@ -385,7 +387,7 @@
     private fun createNewRequest(
         size: Size,
         dynamicRange: DynamicRange = DynamicRange.SDR,
-        expectedFrameRate: Range<Int>? = null,
+        expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED,
         autoCleanup: Boolean = true,
         onInvalidated: () -> Unit = {},
     ): SurfaceRequest {
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 2664daf..ea180a3 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
@@ -230,7 +230,7 @@
                 streamSpec.getResolution(),
                 getCamera(),
                 streamSpec.getDynamicRange(),
-                /* expectedFrameRate= */null,
+                streamSpec.getExpectedFrameRateRange(),
                 this::notifyReset);
         mCurrentSurfaceRequest = surfaceRequest;
 
@@ -287,8 +287,7 @@
 
         // Send the app Surface to the app.
         mSessionDeferrableSurface = mCameraEdge.getDeferrableSurface();
-        mCurrentSurfaceRequest = appEdge.createSurfaceRequest(camera,
-                /* expectedFrameRateRange= */null);
+        mCurrentSurfaceRequest = appEdge.createSurfaceRequest(camera);
         if (mSurfaceProvider != null) {
             // Only send surface request if the provider is set.
             sendSurfaceRequest();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
index 2b7e481..bc20e6b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
@@ -38,6 +38,7 @@
 import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.ImageFormatConstants;
 import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
@@ -86,6 +87,16 @@
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public final class SurfaceRequest {
+
+    /**
+     * A frame rate range with no specified lower or upper bound.
+     *
+     * @see SurfaceRequest#getExpectedFrameRate()
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final Range<Integer> FRAME_RATE_RANGE_UNSPECIFIED =
+            StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED;
+
     private final Object mLock = new Object();
 
     private final Size mResolution;
@@ -93,7 +104,6 @@
     @NonNull
     private final DynamicRange mDynamicRange;
 
-    @Nullable
     private final Range<Integer> mExpectedFrameRate;
     private final CameraInternal mCamera;
 
@@ -136,11 +146,11 @@
             @NonNull Size resolution,
             @NonNull CameraInternal camera,
             @NonNull Runnable onInvalidated) {
-        this(resolution, camera, DynamicRange.SDR, /*expectedFrameRate=*/null, onInvalidated);
+        this(resolution, camera, DynamicRange.SDR, FRAME_RATE_RANGE_UNSPECIFIED, onInvalidated);
     }
 
     /**
-     * Creates a new surface request with the given resolution, {@link Camera}, and an optional
+     * Creates a new surface request with the given resolution, {@link Camera}, dynamic range, and
      * expected frame rate.
      *
      */
@@ -149,7 +159,7 @@
             @NonNull Size resolution,
             @NonNull CameraInternal camera,
             @NonNull DynamicRange dynamicRange,
-            @Nullable Range<Integer> expectedFrameRate,
+            @NonNull Range<Integer> expectedFrameRate,
             @NonNull Runnable onInvalidated) {
         super();
         mResolution = resolution;
@@ -346,15 +356,15 @@
      * conditions. The frame rate may also be fixed, in which case {@link Range#getUpper()} will
      * be equivalent to {@link Range#getLower()}.
      *
-     * <p>This method may also return {@code null} if no information about the frame rate can be
-     * determined. In this case, no assumptions should be made about what the actual frame rate
-     * will be.
+     * <p>This method may also return {@link #FRAME_RATE_RANGE_UNSPECIFIED} if no information about
+     * the frame rate can be determined. In this case, no assumptions should be made about what
+     * the actual frame rate will be.
      *
-     * @return The expected frame rate range or {@code null} if no frame rate information is
-     * available.
+     * @return The expected frame rate range or {@link #FRAME_RATE_RANGE_UNSPECIFIED} if no frame
+     * rate information is available.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Nullable
+    @NonNull
     public Range<Integer> getExpectedFrameRate() {
         return mExpectedFrameRate;
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
index 84cd822..2344f84 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
@@ -31,7 +31,6 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Build;
-import android.util.Range;
 import android.util.Size;
 import android.view.Surface;
 import android.view.SurfaceView;
@@ -238,32 +237,14 @@
     @MainThread
     @NonNull
     public SurfaceRequest createSurfaceRequest(@NonNull CameraInternal cameraInternal) {
-        return createSurfaceRequest(cameraInternal, null);
-    }
-
-    /**
-     * Creates a {@link SurfaceRequest} that is linked to this {@link SurfaceEdge}.
-     *
-     * <p>The {@link SurfaceRequest} is for requesting a {@link Surface} from an external source
-     * such as {@code PreviewView} or {@code VideoCapture}. {@link SurfaceEdge} uses the
-     * {@link Surface} provided by {@link SurfaceRequest#provideSurface} as its source. For how
-     * the ref-counting works, please see the Javadoc of {@link #setProvider}.
-     *
-     * <p>It throws {@link IllegalStateException} if the current {@link SurfaceEdge}
-     * already has a provider.
-     *
-     * <p>This overload optionally allows allows specifying the dynamic range and expected frame
-     * rate range with which the surface should operate.
-     */
-    @MainThread
-    @NonNull
-    public SurfaceRequest createSurfaceRequest(@NonNull CameraInternal cameraInternal,
-            @Nullable Range<Integer> expectedFrameRateRange) {
         checkMainThread();
         checkNotClosed();
         // TODO(b/238230154) figure out how to support HDR.
-        SurfaceRequest surfaceRequest = new SurfaceRequest(mStreamSpec.getResolution(),
-                cameraInternal, mStreamSpec.getDynamicRange(), expectedFrameRateRange,
+        SurfaceRequest surfaceRequest = new SurfaceRequest(
+                mStreamSpec.getResolution(),
+                cameraInternal,
+                mStreamSpec.getDynamicRange(),
+                mStreamSpec.getExpectedFrameRateRange(),
                 () -> mainThreadExecutor().execute(() -> {
                     if (!mIsClosed) {
                         invalidate();
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 218bd59..fe701ff 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -203,6 +203,15 @@
     }
 
     @Test
+    fun surfaceRequestFrameRateRange_isUnspecified() {
+        // Target frame rate range isn't specified, so SurfaceRequest
+        // expected frame rate range should be unspecified.
+        assertThat(bindToLifecycleAndGetSurfaceRequest().expectedFrameRate).isEqualTo(
+            SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
+        )
+    }
+
+    @Test
     fun defaultMirrorModeIsOnFrontOnly() {
         val preview = Preview.Builder().build()
         assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
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 0028e24..286acb9 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
@@ -421,14 +421,22 @@
      * {@link PackageManager#FEATURE_CAMERA_CONCURRENT} or cameras are already used by other
      * {@link UseCase}s, {@link UnsupportedOperationException} will be thrown.
      *
+     * <p>To set up concurrent camera, call {@link #getAvailableConcurrentCameraInfos()} to get
+     * the list of available combinations of concurrent cameras. Each sub-list contains the
+     * {@link CameraInfo}s for a combination of cameras that can be operated concurrently.
+     * Each camera can have its own {@link UseCase}s and {@link LifecycleOwner}. See
+     * <a href="{@docRoot}training/camerax/architecture#lifecycles">CameraX lifecycles</a>
+     *
      * @param singleCameraConfigs input list of {@link SingleCameraConfig}s.
      * @return output {@link ConcurrentCamera} instance.
      *
      * @throws IllegalArgumentException If less or more than two camera configs are provided.
      * @throws UnsupportedOperationException If device is not supporting concurrent camera or
      * cameras are already used by other {@link UseCase}s.
+     *
+     * @see ConcurrentCamera
+     * @see #getAvailableConcurrentCameraInfos()
      */
-    @SuppressWarnings({"lambdaLast"})
     @MainThread
     @NonNull
     public ConcurrentCamera bindToLifecycle(@NonNull List<SingleCameraConfig> singleCameraConfigs) {
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt
index afdaa23..354f38a 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt
@@ -17,6 +17,7 @@
 package androidx.camera.video.internal.config
 
 import android.util.Range
+import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.impl.Timebase
 import androidx.camera.testing.EncoderProfilesUtil
 import androidx.camera.video.VideoSpec
@@ -37,11 +38,11 @@
         val TIMEBASE = Timebase.UPTIME
         const val FRAME_RATE_30 = 30
         const val FRAME_RATE_45 = 45
-        const val FRAME_RATE_60 = 60
+        val DEFAULT_VIDEO_SPEC: VideoSpec by lazy {
+            VideoSpec.builder().build()
+        }
     }
 
-    private val defaultVideoSpec = VideoSpec.builder().build()
-
     @Test
     fun defaultVideoSpecProducesValidSettings_forDifferentSurfaceSizes() {
         val surfaceSizeCif = EncoderProfilesUtil.RESOLUTION_CIF
@@ -54,7 +55,7 @@
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                defaultVideoSpec,
+                DEFAULT_VIDEO_SPEC,
                 surfaceSizeCif,
                 expectedFrameRateRange
             )
@@ -62,7 +63,7 @@
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                defaultVideoSpec,
+                DEFAULT_VIDEO_SPEC,
                 surfaceSize720p,
                 expectedFrameRateRange
             )
@@ -70,7 +71,7 @@
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                defaultVideoSpec,
+                DEFAULT_VIDEO_SPEC,
                 surfaceSize1080p,
                 expectedFrameRateRange
             )
@@ -103,9 +104,9 @@
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                defaultVideoSpec,
+                DEFAULT_VIDEO_SPEC,
                 surfaceSize720p,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get()
         val defaultBitrate = defaultConfig.bitrate
 
@@ -124,7 +125,7 @@
                 TIMEBASE,
                 higherVideoSpec,
                 surfaceSize720p,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isEqualTo(higherBitrate)
 
@@ -134,74 +135,45 @@
                 TIMEBASE,
                 lowerVideoSpec,
                 surfaceSize720p,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isEqualTo(lowerBitrate)
     }
 
     @Test
-    fun frameRateIsChosenFromVideoSpec_whenNoExpectedRangeProvided() {
-        // Give a VideoSpec with a frame rate higher than 30
-        val videoSpec =
-            VideoSpec.builder().setFrameRate(Range(FRAME_RATE_60, FRAME_RATE_60)).build()
+    fun frameRateIsDefault_whenNoExpectedRangeProvided() {
         val size = EncoderProfilesUtil.RESOLUTION_1080P
 
         assertThat(
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                videoSpec,
+                DEFAULT_VIDEO_SPEC,
                 size,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().frameRate
         ).isEqualTo(
-            FRAME_RATE_60
+            VideoEncoderConfigDefaultResolver.VIDEO_FRAME_RATE_FIXED_DEFAULT
         )
     }
 
     @Test
-    fun frameRateIsChosenFromExpectedRange_whenNoOverlapWithVideoSpec() {
-        // Give a VideoSpec with a frame rate higher than 30
-        val videoSpec =
-            VideoSpec.builder().setFrameRate(Range(FRAME_RATE_60, FRAME_RATE_60)).build()
+    fun frameRateIsChosenFromUpperOfExpectedRange_whenProvided() {
         val size = EncoderProfilesUtil.RESOLUTION_1080P
 
-        val expectedFrameRateRange = Range(FRAME_RATE_30, FRAME_RATE_30)
+        val expectedFrameRateRange = Range(FRAME_RATE_30, FRAME_RATE_45)
 
         // Expected frame rate range takes precedence over VideoSpec
         assertThat(
             VideoEncoderConfigDefaultResolver(
                 MIME_TYPE,
                 TIMEBASE,
-                videoSpec,
+                DEFAULT_VIDEO_SPEC,
                 size,
                 expectedFrameRateRange
             ).get().frameRate
         ).isEqualTo(
-            FRAME_RATE_30
+            FRAME_RATE_45
         )
     }
-
-    @Test
-    fun frameRateIsChosenFromOverlapOfExpectedRangeAndVideoSpec() {
-        // Give a VideoSpec with a frame rate higher than 30
-        val videoSpec =
-            VideoSpec.builder().setFrameRate(Range(FRAME_RATE_30, FRAME_RATE_45)).build()
-        val size = EncoderProfilesUtil.RESOLUTION_1080P
-
-        val expectedFrameRateRange = Range(FRAME_RATE_30, FRAME_RATE_60)
-
-        val intersection = expectedFrameRateRange.intersect(videoSpec.frameRate)
-
-        // Expected frame rate range takes precedence over VideoSpec
-        assertThat(
-            VideoEncoderConfigDefaultResolver(
-                MIME_TYPE,
-                TIMEBASE,
-                videoSpec,
-                size,
-                expectedFrameRateRange
-            ).get().frameRate
-        ).isIn(com.google.common.collect.Range.closed(intersection.lower, intersection.upper))
-    }
 }
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolverTest.kt
index 42ff63b..2ebc49b 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolverTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolverTest.kt
@@ -23,6 +23,7 @@
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraXConfig
+import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.impl.Timebase
 import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.testing.CameraPipeConfigTestRule
@@ -114,7 +115,7 @@
                 defaultVideoSpec,
                 Size(videoProfile.width, videoProfile.height),
                 videoProfile,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get()
 
             assertThat(config.mimeType).isEqualTo(videoProfile.mediaType)
@@ -135,7 +136,7 @@
             defaultVideoSpec,
             surfaceSize,
             profile,
-            /*expectedFrameRateRange=*/null
+            SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
         ).get().bitrate
 
         val increasedSurfaceSize = Size(surfaceSize.width + 100, surfaceSize.height + 100)
@@ -148,7 +149,7 @@
                 defaultVideoSpec,
                 increasedSurfaceSize,
                 profile,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isGreaterThan(defaultBitrate)
 
@@ -159,7 +160,7 @@
                 defaultVideoSpec,
                 decreasedSurfaceSize,
                 profile,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isLessThan(defaultBitrate)
     }
@@ -175,7 +176,7 @@
             defaultVideoSpec,
             surfaceSize,
             profile,
-            /*expectedFrameRateRange=*/null
+            SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
         ).get().bitrate
 
         // Create video spec with limit 20% higher than default.
@@ -194,7 +195,7 @@
                 higherVideoSpec,
                 surfaceSize,
                 profile,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isEqualTo(higherBitrate)
 
@@ -205,7 +206,7 @@
                 lowerVideoSpec,
                 surfaceSize,
                 profile,
-                /*expectedFrameRateRange=*/null
+                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
             ).get().bitrate
         ).isEqualTo(lowerBitrate)
     }
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/workaround/EncoderFinderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/workaround/EncoderFinderTest.kt
index 1369ac2..04d5d9b 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/workaround/EncoderFinderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/workaround/EncoderFinderTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.core.Camera
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraXConfig
+import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.impl.CameraInfoInternal
 import androidx.camera.core.impl.Timebase
 import androidx.camera.testing.CameraPipeConfigTestRule
@@ -165,7 +166,7 @@
             videoSpec,
             resolution!!,
             videoProfile,
-            /*expectedFrameRateRange=*/null
+            SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
         ).get().toMediaFormat()
 
         // Act.
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 5b5ced9..576c0ff 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
@@ -132,6 +132,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CancellationException;
@@ -569,16 +570,19 @@
         // handleInvalidate() can be used as an alternative.
         Runnable onSurfaceInvalidated = this::notifyReset;
 
-        // TODO(b/229410005): The expected FPS range will need to come from the camera rather
-        //  than what is requested in the config. For now we use the default range of (30, 30)
-        //  for behavioral consistency.
-        Range<Integer> targetFpsRange = requireNonNull(
-                config.getTargetFrameRate(Defaults.DEFAULT_FPS_RANGE));
+        // If the expected frame rate range is unspecified, we need to give an educated estimate
+        // on what frame rate the camera will be operating at. For most devices this is a
+        // constant frame rate of 30fps, but in the future this could probably be queried from
+        // the camera.
+        Range<Integer> expectedFrameRate = streamSpec.getExpectedFrameRateRange();
+        if (Objects.equals(expectedFrameRate, StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)) {
+            expectedFrameRate = Defaults.DEFAULT_FPS_RANGE;
+        }
         MediaSpec mediaSpec = requireNonNull(getMediaSpec());
         LegacyVideoCapabilities videoCapabilities = LegacyVideoCapabilities.from(
                 camera.getCameraInfo());
         VideoEncoderInfo videoEncoderInfo = getVideoEncoderInfo(config.getVideoEncoderInfoFinder(),
-                videoCapabilities, mediaSpec, resolution, targetFpsRange);
+                videoCapabilities, mediaSpec, resolution, expectedFrameRate);
         mCropRect = calculateCropRect(resolution, videoEncoderInfo);
         mNode = createNodeIfNeeded(camera, mCropRect, resolution);
         // Choose Timebase based on the whether the buffer is copied.
@@ -596,10 +600,13 @@
         if (mNode != null) {
             // Make sure the previously created camera edge is cleared before creating a new one.
             checkState(mCameraEdge == null);
+            // Update the StreamSpec to use the frame rate range that is not unspecified.
+            StreamSpec updatedStreamSpec =
+                    streamSpec.toBuilder().setExpectedFrameRateRange(expectedFrameRate).build();
             SurfaceEdge cameraEdge = new SurfaceEdge(
                     VIDEO_CAPTURE,
                     INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
-                    streamSpec,
+                    updatedStreamSpec,
                     getSensorToBufferTransformMatrix(),
                     camera.getHasTransform(),
                     mCropRect,
@@ -616,7 +623,7 @@
             SurfaceEdge appEdge = requireNonNull(nodeOutput.get(outConfig));
             appEdge.addOnInvalidatedListener(
                     () -> onAppEdgeInvalidated(appEdge, camera, config, timebase));
-            mSurfaceRequest = appEdge.createSurfaceRequest(camera, targetFpsRange);
+            mSurfaceRequest = appEdge.createSurfaceRequest(camera);
             mDeferrableSurface = cameraEdge.getDeferrableSurface();
             DeferrableSurface latestDeferrableSurface = mDeferrableSurface;
             mDeferrableSurface.getTerminationFuture().addListener(() -> {
@@ -631,7 +638,7 @@
                     resolution,
                     camera,
                     streamSpec.getDynamicRange(),
-                    targetFpsRange,
+                    expectedFrameRate,
                     onSurfaceInvalidated);
             mDeferrableSurface = mSurfaceRequest.getDeferrableSurface();
         }
@@ -644,6 +651,8 @@
 
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config,
                 streamSpec.getResolution());
+        // Use the frame rate range directly from the StreamSpec here (don't resolve it to the
+        // default if unresolved).
         sessionConfigBuilder.setExpectedFrameRateRange(streamSpec.getExpectedFrameRateRange());
         sessionConfigBuilder.addErrorListener(
                 (sessionConfig, error) -> resetPipeline(cameraId, config, streamSpec));
@@ -1031,7 +1040,7 @@
             @NonNull LegacyVideoCapabilities videoCapabilities,
             @NonNull MediaSpec mediaSpec,
             @NonNull Size resolution,
-            @NonNull Range<Integer> targetFps) {
+            @NonNull Range<Integer> expectedFrameRate) {
         if (mVideoEncoderInfo != null) {
             return mVideoEncoderInfo;
         }
@@ -1040,7 +1049,7 @@
         VideoValidatedEncoderProfilesProxy encoderProfiles =
                 videoCapabilities.findHighestSupportedEncoderProfilesFor(resolution);
         VideoEncoderInfo videoEncoderInfo = resolveVideoEncoderInfo(videoEncoderInfoFinder,
-                encoderProfiles, mediaSpec, resolution, targetFps);
+                encoderProfiles, mediaSpec, resolution, expectedFrameRate);
         if (videoEncoderInfo == null) {
             // If VideoCapture cannot find videoEncoderInfo, it means that VideoOutput should
             // also not be able to find the encoder. VideoCapture will not handle this situation
@@ -1068,7 +1077,7 @@
             @Nullable VideoValidatedEncoderProfilesProxy encoderProfiles,
             @NonNull MediaSpec mediaSpec,
             @NonNull Size resolution,
-            @NonNull Range<Integer> targetFps) {
+            @NonNull Range<Integer> expectedFrameRate) {
         // Resolve the VideoEncoderConfig
         MimeInfo videoMimeInfo = resolveVideoMimeInfo(mediaSpec, encoderProfiles);
         VideoEncoderConfig videoEncoderConfig = resolveVideoEncoderConfig(
@@ -1077,7 +1086,7 @@
                 Timebase.UPTIME,
                 mediaSpec.getVideoSpec(),
                 resolution,
-                targetFps);
+                expectedFrameRate);
 
         return videoEncoderInfoFinder.apply(videoEncoderConfig);
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoRecordEvent.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoRecordEvent.java
index 5859263..507f856 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoRecordEvent.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoRecordEvent.java
@@ -417,6 +417,7 @@
                 case ERROR_RECORDER_ERROR: return "ERROR_RECORDER_ERROR";
                 case ERROR_NO_VALID_DATA: return "ERROR_NO_VALID_DATA";
                 case ERROR_SOURCE_INACTIVE: return "ERROR_SOURCE_INACTIVE";
+                case ERROR_DURATION_LIMIT_REACHED: return "ERROR_DURATION_LIMIT_REACHED";
             }
 
             // Should never reach here, but just in case...
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java
index 8d3be191..5393edc 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java
@@ -106,13 +106,13 @@
      * @param videoSpec              the video spec.
      * @param inputTimebase          the timebase of the input frame.
      * @param surfaceSize            the surface size.
-     * @param expectedFrameRateRange the expected frame rate range. It could be null.
+     * @param expectedFrameRateRange the expected frame rate range.
      * @return a VideoEncoderConfig.
      */
     @NonNull
     public static VideoEncoderConfig resolveVideoEncoderConfig(@NonNull MimeInfo videoMimeInfo,
             @NonNull Timebase inputTimebase, @NonNull VideoSpec videoSpec,
-            @NonNull Size surfaceSize, @Nullable Range<Integer> expectedFrameRateRange) {
+            @NonNull Size surfaceSize, @NonNull Range<Integer> expectedFrameRateRange) {
         Supplier<VideoEncoderConfig> configSupplier;
         VideoValidatedEncoderProfilesProxy profiles = videoMimeInfo.getCompatibleEncoderProfiles();
         if (profiles != null) {
@@ -127,37 +127,6 @@
         return configSupplier.get();
     }
 
-    static int resolveFrameRate(@NonNull Range<Integer> preferredRange,
-            int exactFrameRateHint, @Nullable Range<Integer> strictOperatingFpsRange) {
-        Range<Integer> refinedRange;
-        if (strictOperatingFpsRange != null) {
-            // We have a strict operating range. Our frame rate should always be in this
-            // range. Since we can only choose a single frame rate (which acts as a target for
-            // VBR), we can only fine tune our preferences within that range.
-            try {
-                // First, let's try to intersect with the preferred frame rate range since this
-                // could contain intent from the user.
-                refinedRange = strictOperatingFpsRange.intersect(preferredRange);
-            } catch (IllegalArgumentException ex) {
-                // Ranges are disjoint. Choose the closest extreme as our frame rate.
-                if (preferredRange.getUpper() < strictOperatingFpsRange.getLower()) {
-                    // Preferred range is below operating range.
-                    return strictOperatingFpsRange.getLower();
-                } else {
-                    // Preferred range is above operating range.
-                    return strictOperatingFpsRange.getUpper();
-                }
-            }
-        } else {
-            // We only have the preferred range as a hint since the operating range is null.
-            refinedRange = preferredRange;
-        }
-
-        // Finally, try to apply the exact frame rate hint to the refined range since
-        // other settings may expect this number.
-        return refinedRange.clamp(exactFrameRateHint);
-    }
-
     static int scaleAndClampBitrate(
             int baseBitrate,
             int actualFrameRate, int baseFrameRate,
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java
index 75d159f..7d09ed2 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java
@@ -20,14 +20,16 @@
 import android.util.Size;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.Logger;
+import androidx.camera.core.SurfaceRequest;
 import androidx.camera.core.impl.Timebase;
 import androidx.camera.video.VideoSpec;
 import androidx.camera.video.internal.encoder.VideoEncoderConfig;
 import androidx.core.util.Supplier;
 
+import java.util.Objects;
+
 /**
  * A {@link VideoEncoderConfig} supplier that resolves requested encoder settings from a
  * {@link VideoSpec} for the given surface {@link Size} using pre-defined default values.
@@ -42,7 +44,7 @@
     private static final int VIDEO_BITRATE_BASE = 14000000;
     private static final Size VIDEO_SIZE_BASE = new Size(1280, 720);
     private static final int VIDEO_FRAME_RATE_BASE = 30;
-    private static final int VIDEO_FRAME_RATE_FIXED_DEFAULT = 30;
+    static final int VIDEO_FRAME_RATE_FIXED_DEFAULT = 30;
     private static final Range<Integer> VALID_FRAME_RATE_RANGE = new Range<>(1, 60);
 
     private final String mMimeType;
@@ -50,21 +52,28 @@
     private final Timebase mInputTimebase;
     private final VideoSpec mVideoSpec;
     private final Size mSurfaceSize;
-    @Nullable
     private final Range<Integer> mExpectedFrameRateRange;
 
     /**
      * Constructor for a VideoEncoderConfigDefaultResolver.
      *
-     * @param mimeType      The mime type for the video encoder
-     * @param inputTimebase The time base of the input frame.
-     * @param videoSpec     The {@link VideoSpec} which defines the settings that should be used
-     *                      with the video encoder.
-     * @param surfaceSize   The size of the surface required by the camera for the video encoder.
+     * @param mimeType               The mime type for the video encoder
+     * @param inputTimebase          The time base of the input frame.
+     * @param videoSpec              The {@link VideoSpec} which defines the settings that should
+     *                               be used with the video encoder.
+     * @param surfaceSize            The size of the surface required by the camera for the video
+     *                               encoder.
+     * @param expectedFrameRateRange The expected source frame rate range. This should act as an
+     *                               envelope for any frame rate calculated from {@code videoSpec
+     *                               } and {@code videoProfile} since the source should not
+     *                               produce frames at a frame rate outside this range. If
+     *                               equal to {@link SurfaceRequest#FRAME_RATE_RANGE_UNSPECIFIED},
+     *                               then no information about the source frame rate is available
+     *                               and it does not need to be used in calculations.
      */
     public VideoEncoderConfigDefaultResolver(@NonNull String mimeType,
             @NonNull Timebase inputTimebase, @NonNull VideoSpec videoSpec,
-            @NonNull Size surfaceSize, @Nullable Range<Integer> expectedFrameRateRange) {
+            @NonNull Size surfaceSize, @NonNull Range<Integer> expectedFrameRateRange) {
         mMimeType = mimeType;
         mInputTimebase = inputTimebase;
         mVideoSpec = videoSpec;
@@ -98,26 +107,23 @@
     }
 
     private int resolveFrameRate() {
-        Range<Integer> videoSpecFrameRateRange = mVideoSpec.getFrameRate();
-        // If the frame rate range isn't AUTO, we'll use the upper frame rate from the video spec
-        // as our default in an attempt to maximize the quality of the video. However, we need to
-        // ensure it is a valid frame rate, so clamp between 1 and 60fps.
-        int defaultFrameRate;
-        if (!VideoSpec.FRAME_RATE_RANGE_AUTO.equals(videoSpecFrameRateRange)) {
-            defaultFrameRate = VALID_FRAME_RATE_RANGE.clamp(videoSpecFrameRateRange.getUpper());
+        // If the operating frame rate range isn't unspecified, we'll use the upper frame rate from
+        // as our default in an attempt to maximize the quality of the video. Clamp the value to
+        // ensure it's a valid frame rate.
+        int resolvedFrameRate;
+        if (!Objects.equals(mExpectedFrameRateRange, SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED)) {
+            resolvedFrameRate = VALID_FRAME_RATE_RANGE.clamp(mExpectedFrameRateRange.getUpper());
         } else {
-            // We have no information to base the frame rate on. Use a standard default.
-            defaultFrameRate = VIDEO_FRAME_RATE_FIXED_DEFAULT;
+            // If the frame rate range is unspecified, return a hard coded common default.
+            resolvedFrameRate = VIDEO_FRAME_RATE_FIXED_DEFAULT;
         }
 
         Logger.d(TAG,
-                String.format("Frame rate default: %dfps. [Requested range: %s, "
-                                + "Expected operating range: %s]", defaultFrameRate,
-                        videoSpecFrameRateRange, mExpectedFrameRateRange));
+                String.format("Default resolved frame rate: %dfps. [Expected operating range: %s]",
+                        resolvedFrameRate, Objects.equals(mExpectedFrameRateRange,
+                                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED)
+                                ? mExpectedFrameRateRange : "<UNSPECIFIED>"));
 
-        return VideoConfigUtil.resolveFrameRate(
-                /*preferredRange=*/ videoSpecFrameRateRange,
-                /*exactFrameRateHint=*/ defaultFrameRate,
-                /*strictOperatingFpsRange=*/mExpectedFrameRateRange);
+        return resolvedFrameRate;
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolver.java
index 0009f26..dc374ae 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolver.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolver.java
@@ -20,15 +20,17 @@
 import android.util.Size;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.Logger;
+import androidx.camera.core.SurfaceRequest;
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
 import androidx.camera.core.impl.Timebase;
 import androidx.camera.video.VideoSpec;
 import androidx.camera.video.internal.encoder.VideoEncoderConfig;
 import androidx.core.util.Supplier;
 
+import java.util.Objects;
+
 /**
  * A {@link VideoEncoderConfig} supplier that resolves requested encoder settings from a
  * {@link VideoSpec} for the given surface {@link Size} using the provided
@@ -44,7 +46,6 @@
     private final VideoSpec mVideoSpec;
     private final Size mSurfaceSize;
     private final VideoProfileProxy mVideoProfile;
-    @Nullable
     private final Range<Integer> mExpectedFrameRateRange;
 
     /**
@@ -58,18 +59,19 @@
      * @param videoProfile     The {@link VideoProfileProxy} used to resolve automatic and range
      *                         settings.
      * @param expectedFrameRateRange The expected source frame rate range. This should act as an
-     *                               envelope for any frame rate calculated from {@code videoSpec
-     *                               } and {@code videoProfile} since the source should not
-     *                               produce frames at a frame rate outside this range. If {@code
-     *                               null}, then no information about the source frame rate is
-     *                               available and it does not need to be used in calculations.
+     *                               envelope for any frame rate calculated from {@code videoSpec}
+     *                               and {@code videoProfile} since the source should not
+     *                               produce frames at a frame rate outside this range. If
+     *                               equal to {@link SurfaceRequest#FRAME_RATE_RANGE_UNSPECIFIED},
+     *                               then no information about the source frame rate is available
+     *                               and it does not need to be used in calculations.
      */
     public VideoEncoderConfigVideoProfileResolver(@NonNull String mimeType,
             @NonNull Timebase inputTimebase,
             @NonNull VideoSpec videoSpec,
             @NonNull Size surfaceSize,
             @NonNull VideoProfileProxy videoProfile,
-            @Nullable Range<Integer> expectedFrameRateRange) {
+            @NonNull Range<Integer> expectedFrameRateRange) {
         mMimeType = mimeType;
         mInputTimebase = inputTimebase;
         mVideoSpec = videoSpec;
@@ -103,16 +105,21 @@
     }
 
     private int resolveFrameRate() {
-        Range<Integer> videoSpecFrameRateRange = mVideoSpec.getFrameRate();
         int videoProfileFrameRate = mVideoProfile.getFrameRate();
-        Logger.d(TAG,
-                String.format("Frame rate from video profile: %dfps. [Requested range: %s, "
-                        + "Expected operating range: %s]", videoProfileFrameRate,
-                        videoSpecFrameRateRange, mExpectedFrameRateRange));
+        int resolvedFrameRate;
+        if (!Objects.equals(mExpectedFrameRateRange, SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED)) {
+            resolvedFrameRate = mExpectedFrameRateRange.clamp(videoProfileFrameRate);
+        } else {
+            resolvedFrameRate = videoProfileFrameRate;
+        }
 
-        return VideoConfigUtil.resolveFrameRate(
-                /*preferredRange=*/ videoSpecFrameRateRange,
-                /*exactFrameRateHint=*/ videoProfileFrameRate,
-                /*strictOperatingFpsRange=*/mExpectedFrameRateRange);
+        Logger.d(TAG,
+                String.format("Resolved frame rate %dfps [Video profile frame rate: %dfps, "
+                                + "Expected operating range: %s]", resolvedFrameRate,
+                        videoProfileFrameRate, Objects.equals(mExpectedFrameRateRange,
+                                SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED)
+                                ? mExpectedFrameRateRange : "<UNSPECIFIED>"));
+
+        return resolvedFrameRate;
     }
 }
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 d0e5fb9..48d03ab 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
@@ -43,9 +43,9 @@
 import androidx.camera.core.CameraSelector.LENS_FACING_BACK
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
 import androidx.camera.core.CameraXConfig
-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
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CameraFactory
@@ -1038,6 +1038,27 @@
         )
     }
 
+    @Test
+    fun suggestedStreamSpecFrameRate_isPropagatedToSurfaceRequest() {
+        // [24, 24] is what will be chosen by the stream spec. By setting the target to another
+        // value, this ensures the SurfaceRequest is getting what comes from the stream spec rather
+        // than just from the target.
+        testSurfaceRequestContainsExpected(
+            targetFrameRate = FRAME_RATE_RANGE_FIXED_30,
+            expectedFrameRate = FRAME_RATE_RANGE_FIXED_24
+        )
+    }
+
+    @Test
+    fun unspecifiedStreamSpecFrameRate_sendsDefaultFrameRateToSurfaceRequest() {
+        // Currently we assume a fixed [30, 30] for VideoCapture since that is typically the fixed
+        // frame rate that most devices will choose for a video template. In the future we may
+        // try to query the device for this default frame rate.
+        testSurfaceRequestContainsExpected(
+            expectedFrameRate = FRAME_RATE_RANGE_FIXED_30
+        )
+    }
+
     private fun testSetTargetRotation_transformationInfoUpdated(
         lensFacing: Int = LENS_FACING_BACK,
         sensorRotationDegrees: Int = 0,
@@ -1260,34 +1281,63 @@
     private fun testAdjustCropRectToValidSize(
         quality: Quality = HD, // HD maps to 1280x720 (4:3)
         videoEncoderInfo: VideoEncoderInfo = createVideoEncoderInfo(),
-        cropRect: Rect,
-        expectedCropRect: Rect,
+        cropRect: Rect? = null,
+        expectedCropRect: Rect? = null
+    ) {
+        testSurfaceRequestContainsExpected(
+            quality = quality,
+            videoEncoderInfo = videoEncoderInfo,
+            cropRect = cropRect,
+            expectedCropRect = expectedCropRect
+        )
+    }
+
+    private fun testSurfaceRequestContainsExpected(
+        quality: Quality = HD, // HD maps to 1280x720 (4:3)
+        videoEncoderInfo: VideoEncoderInfo = createVideoEncoderInfo(),
+        cropRect: Rect? = null,
+        expectedCropRect: Rect? = null,
+        targetFrameRate: Range<Int>? = null,
+        expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
     ) {
         // Arrange.
         setupCamera()
         createCameraUseCaseAdapter()
-        setSuggestedStreamSpec(quality)
+        setSuggestedStreamSpec(
+            quality,
+            expectedFrameRate = expectedFrameRate
+        )
         var surfaceRequest: SurfaceRequest? = null
         val videoOutput = createVideoOutput(
             mediaSpec = MediaSpec.builder().configureVideo {
                 it.setQualitySelector(QualitySelector.from(quality))
             }.build(),
-            surfaceRequestListener = { request, _ -> surfaceRequest = request }
+            surfaceRequestListener = { request, _ -> surfaceRequest = request },
         )
         val videoCapture = createVideoCapture(
             videoOutput,
-            videoEncoderInfoFinder = { videoEncoderInfo }
+            videoEncoderInfoFinder = { videoEncoderInfo },
+            targetFrameRate = targetFrameRate
         )
-        cameraUseCaseAdapter.setEffects(listOf(createFakeEffect()))
-        videoCapture.setViewPortCropRect(cropRect)
+
+        cropRect?.let {
+            cameraUseCaseAdapter.setEffects(listOf(createFakeEffect()))
+            videoCapture.setViewPortCropRect(it)
+        }
 
         // Act.
         addAndAttachUseCases(videoCapture)
 
         // Assert.
         assertThat(surfaceRequest).isNotNull()
-        assertThat(surfaceRequest!!.resolution).isEqualTo(rectToSize(expectedCropRect))
-        assertThat(videoCapture.cropRect).isEqualTo(expectedCropRect)
+        expectedCropRect?.let {
+            assertThat(surfaceRequest!!.resolution).isEqualTo(rectToSize(it))
+            assertThat(videoCapture.cropRect).isEqualTo(it)
+        }
+
+        if (expectedFrameRate != StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED) {
+            assertThat(surfaceRequest!!.expectedFrameRate).isEqualTo(expectedFrameRate)
+        }
     }
 
     private fun assertCustomOrderedResolutions(
@@ -1377,6 +1427,7 @@
         targetRotation: Int? = null,
         mirrorMode: Int? = null,
         targetResolution: Size? = null,
+        targetFrameRate: Range<Int>? = null,
         videoEncoderInfoFinder: Function<VideoEncoderConfig, VideoEncoderInfo> =
             Function { createVideoEncoderInfo() },
     ): VideoCapture<VideoOutput> = VideoCapture.Builder(videoOutput)
@@ -1385,6 +1436,7 @@
             targetRotation?.let { setTargetRotation(it) }
             mirrorMode?.let { setMirrorMode(it) }
             targetResolution?.let { setTargetResolution(it) }
+            targetFrameRate?.let { setTargetFrameRate(it) }
             setVideoEncoderInfoFinder(videoEncoderInfoFinder)
         }.build()
 
@@ -1409,8 +1461,14 @@
         return handler
     }
 
-    private fun setSuggestedStreamSpec(quality: Quality) {
-        setSuggestedStreamSpec(StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!).build())
+    private fun setSuggestedStreamSpec(
+        quality: Quality,
+        expectedFrameRate: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED
+    ) {
+        setSuggestedStreamSpec(
+            StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!)
+                .setExpectedFrameRateRange(expectedFrameRate).build()
+        )
     }
 
     private fun setSuggestedStreamSpec(streamSpec: StreamSpec) {
@@ -1476,6 +1534,9 @@
             HIGHEST to RESOLUTION_2160P,
         )
 
+        private val FRAME_RATE_RANGE_FIXED_24 = Range(24, 24)
+        private val FRAME_RATE_RANGE_FIXED_30 = Range(30, 30)
+
         private val CAMERA_0_SUPPORTED_RESOLUTION_MAP = mapOf(
             ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE to listOf(
                 // 4:3
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateDemoScreen.java
index 63300fa..b6f22ed 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateDemoScreen.java
@@ -120,20 +120,17 @@
                             ICON_RES_IDS[i])).build())
                     .setContentId(contentId);
             if (TextUtils.isEmpty(mActiveContentId) && i == 0) {
-                tabBuilder.setActive(true);
+                mActiveContentId = contentId;
                 mTabTemplateBuilder.setTabContents(tabContents);
             } else if (TextUtils.equals(mActiveContentId, contentId)) {
-                tabBuilder.setActive(true);
                 mTabTemplateBuilder.setTabContents(tabContents);
-            } else {
-                tabBuilder.setActive(false);
             }
 
             Tab tab = tabBuilder.build();
             mTabs.put(tab.getContentId(), tab);
             mTabTemplateBuilder.addTab(tab);
         }
-        return mTabTemplateBuilder.build();
+        return mTabTemplateBuilder.setActiveTabContentId(mActiveContentId).build();
     }
 
     private ListTemplate createListTemplate() {
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateLoadingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateLoadingDemoScreen.java
index ee434b7..2c961fd 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateLoadingDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/tabtemplates/TabTemplateLoadingDemoScreen.java
@@ -86,18 +86,14 @@
                             sIconResIds[i])).build())
                     .setContentId(contentId);
             if (TextUtils.isEmpty(mActiveContentId) && i == 0) {
-                tabBuilder.setActive(true);
-            } else if (TextUtils.equals(mActiveContentId, contentId)) {
-                tabBuilder.setActive(true);
-            } else {
-                tabBuilder.setActive(false);
+                mActiveContentId = contentId;
             }
 
             Tab tab = tabBuilder.build();
             mTabs.put(tab.getContentId(), tab);
             mTabTemplateBuilder.addTab(tab);
 
-            if (tab.isActive()) {
+            if (TextUtils.equals(mActiveContentId, contentId)) {
                 if (i == 0) {
                     mTabTemplateBuilder.setLoading(true);
                 } else {
@@ -105,7 +101,7 @@
                 }
             }
         }
-        return mTabTemplateBuilder.build();
+        return mTabTemplateBuilder.setActiveTabContentId(mActiveContentId).build();
     }
 
     private TabContents createSearchTab() {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 9812488..33ffc83 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -1170,10 +1170,13 @@
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public java.util.List<androidx.car.app.model.Action!> getActions();
     method public androidx.car.app.model.Action? getHeaderAction();
+    method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemImageShape();
     method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemSize();
     method public androidx.car.app.model.ItemList? getSingleList();
     method public androidx.car.app.model.CarText? getTitle();
     method public boolean isLoading();
+    field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_CIRCLE = 2; // 0x2
+    field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_UNSET = 1; // 0x1
     field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_LARGE = 4; // 0x4
     field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_MEDIUM = 2; // 0x2
     field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_SMALL = 1; // 0x1
@@ -1185,6 +1188,7 @@
     method public androidx.car.app.model.GridTemplate build();
     method public androidx.car.app.model.GridTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
     method public androidx.car.app.model.GridTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+    method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemImageShape(int);
     method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemSize(int);
     method public androidx.car.app.model.GridTemplate.Builder setLoading(boolean);
     method public androidx.car.app.model.GridTemplate.Builder setSingleList(androidx.car.app.model.ItemList);
@@ -1525,14 +1529,14 @@
     method public String getContentId();
     method public androidx.car.app.model.CarIcon getIcon();
     method public androidx.car.app.model.CarText getTitle();
-    method public boolean isActive();
+    method @Deprecated public boolean isActive();
     method public androidx.car.app.model.Tab.Builder toBuilder();
   }
 
   public static final class Tab.Builder {
     ctor public Tab.Builder();
     method public androidx.car.app.model.Tab build();
-    method public androidx.car.app.model.Tab.Builder setActive(boolean);
+    method @Deprecated public androidx.car.app.model.Tab.Builder setActive(boolean);
     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);
@@ -1554,6 +1558,7 @@
   }
 
   @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 {
+    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();
@@ -1563,8 +1568,10 @@
 
   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);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
index eac356c..2706095 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
@@ -66,8 +66,6 @@
      * <p>The host decides how to map these size buckets to dimensions. The grid item image size
      * and grid item width will vary by bucket, and the number of items per row
      * will be adjusted according to bucket and screen size.
-     *
-     * @hide
      */
     @ExperimentalCarApi
     @RequiresCarApi(7)
@@ -109,6 +107,43 @@
     @RequiresCarApi(7)
     public static final int ITEM_SIZE_LARGE = (1 << 2);
 
+    /**
+     * The shape of each grid item image contained within this GridTemplate.
+     *
+     * <p>Grid item images will be cropped by the host to match the shape type.
+     */
+    @ExperimentalCarApi
+    @RequiresCarApi(7)
+    @IntDef(
+            value = {
+                    ITEM_IMAGE_SHAPE_UNSET,
+                    ITEM_IMAGE_SHAPE_CIRCLE,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface ItemImageShape {
+    }
+
+    /**
+     * Represents a preference to keep the images as-is without modifying their shape.
+     *
+     * <p>This is the default setting.
+     *
+     * @see GridTemplate.Builder#setItemImageShape(int)
+     */
+    @ExperimentalCarApi
+    @RequiresCarApi(7)
+    public static final int ITEM_IMAGE_SHAPE_UNSET = (1 << 0);
+
+    /**
+     * Represents a preference to crop all grid item images into the shape of a circle.
+     *
+     * @see GridTemplate.Builder#setItemImageShape(int)
+     */
+    @ExperimentalCarApi
+    @RequiresCarApi(7)
+    public static final int ITEM_IMAGE_SHAPE_CIRCLE = (1 << 1);
+
     private final boolean mIsLoading;
     @Nullable
     private final CarText mTitle;
@@ -121,6 +156,8 @@
     private final List<Action> mActions;
     @ItemSize
     private final int mItemSize;
+    @ItemImageShape
+    private final int mItemImageShape;
 
     /**
      * Returns the title of the template or {@code null} if not set.
@@ -197,6 +234,20 @@
         return mItemSize;
     }
 
+    /**
+     * Returns the item image shape.
+     *
+     * <p>All item images in the grid are cropped into the specified shape.
+     *
+     * @see GridTemplate.Builder#setItemImageShape(int)
+     */
+    @ExperimentalCarApi
+    @ItemImageShape
+    @RequiresCarApi(7)
+    public int getItemImageShape() {
+        return mItemImageShape;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -206,7 +257,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mIsLoading, mTitle, mHeaderAction, mSingleList, mActionStrip,
-                mItemSize);
+                mItemSize, mItemImageShape);
     }
 
     @Override
@@ -225,7 +276,8 @@
                 && Objects.equals(mSingleList, otherTemplate.mSingleList)
                 && Objects.equals(mActionStrip, otherTemplate.mActionStrip)
                 && Objects.equals(mActions, otherTemplate.mActions)
-                && mItemSize == otherTemplate.mItemSize;
+                && mItemSize == otherTemplate.mItemSize
+                && mItemImageShape == otherTemplate.mItemImageShape;
     }
 
     GridTemplate(Builder builder) {
@@ -236,6 +288,7 @@
         mActionStrip = builder.mActionStrip;
         mActions = CollectionUtils.unmodifiableCopy(builder.mActions);
         mItemSize = builder.mItemSize;
+        mItemImageShape = builder.mItemImageShape;
     }
 
     /** Constructs an empty instance, used by serialization code. */
@@ -248,6 +301,7 @@
         mActionStrip = null;
         mActions = Collections.emptyList();
         mItemSize = ITEM_SIZE_SMALL;
+        mItemImageShape = ITEM_IMAGE_SHAPE_UNSET;
     }
 
     /** A builder of {@link GridTemplate}. */
@@ -265,6 +319,7 @@
         final List<Action> mActions = new ArrayList<>();
         @ItemSize
         int mItemSize = ITEM_SIZE_SMALL;
+        @ItemImageShape int mItemImageShape = ITEM_IMAGE_SHAPE_UNSET;
 
         /**
          * Sets whether the template is in a loading state.
@@ -393,6 +448,22 @@
         }
 
         /**
+         * Sets the item image shape for this template.
+         *
+         * <p>Grid item images will all be cropped to the specified shape. If set to
+         * ITEM_IMAGE_SHAPE_UNSET, the images will be rendered as-is without changing the shape.
+         *
+         * <p>If not set, default to ITEM_IMAGE_SHAPE_UNSET.
+         */
+        @ExperimentalCarApi
+        @NonNull
+        @RequiresCarApi(7)
+        public Builder setItemImageShape(@ItemImageShape int itemImageShape) {
+            mItemImageShape = itemImageShape;
+            return this;
+        }
+
+        /**
          * Constructs the template defined by this builder.
          *
          * <h4>Requirements</h4>
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 732d534..b6f6089 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
@@ -22,10 +22,10 @@
 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;
 import androidx.car.app.model.constraints.CarTextConstraints;
-import androidx.car.app.annotations.KeepFields;
 
 import java.util.Objects;
 
@@ -84,7 +84,9 @@
      * Indicates if this is the currently active tab.
      *
      * @see Tab.Builder#setActive(boolean)
+     * @deprecated use {@link TabTemplate#getActiveTabContentId()} instead.
      */
+    @Deprecated
     public boolean isActive() {
         return mIsActive;
     }
@@ -226,8 +228,11 @@
 
         /**
          * Sets the active state of the tab.
+         *
+         * @deprecated use {@link TabTemplate.Builder#setActiveTabContentId(String)} instead.
          */
         @NonNull
+        @Deprecated
         public Tab.Builder setActive(boolean isActive) {
             mIsActive = isActive;
             return this;
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 e3fb36f..1a55793 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
@@ -24,11 +24,12 @@
 
 import androidx.annotation.NonNull;
 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;
-import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.utils.CollectionUtils;
 
 import java.util.ArrayList;
@@ -81,6 +82,8 @@
     private final TabContents mTabContents;
     @Nullable
     private final List<Tab> mTabs;
+    @Nullable
+    private final String mActiveTabContentId;
 
     /**
      * Returns the {@link Action} that is set to be displayed in the header of the template, or
@@ -126,6 +129,14 @@
         return requireNonNull(mTabCallbackDelegate);
     }
 
+    /**
+     * Returns the {@link Tab#getContentId()} for the active tab.
+     */
+    @NonNull
+    public String getActiveTabContentId() {
+        return requireNonNull(mActiveTabContentId);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -134,7 +145,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIsLoading, mHeaderAction, mTabs, mTabContents);
+        return Objects.hash(mIsLoading, mHeaderAction, mTabs, mTabContents, mActiveTabContentId);
     }
 
     @Override
@@ -150,7 +161,8 @@
         return mIsLoading == otherTemplate.mIsLoading
                 && Objects.equals(mHeaderAction, otherTemplate.mHeaderAction)
                 && Objects.equals(mTabs, otherTemplate.mTabs)
-                && Objects.equals(mTabContents, otherTemplate.mTabContents);
+                && Objects.equals(mTabContents, otherTemplate.mTabContents)
+                && Objects.equals(mActiveTabContentId, otherTemplate.getActiveTabContentId());
     }
 
     TabTemplate(TabTemplate.Builder builder) {
@@ -159,6 +171,7 @@
         mTabs = CollectionUtils.unmodifiableCopy(builder.mTabs);
         mTabContents = builder.mTabContents;
         mTabCallbackDelegate = builder.mTabCallbackDelegate;
+        mActiveTabContentId = builder.mActiveTabContentId;
     }
 
     /** Constructs an empty instance, used by serialization code. */
@@ -168,6 +181,7 @@
         mTabs = Collections.emptyList();
         mTabContents = null;
         mTabCallbackDelegate = null;
+        mActiveTabContentId = null;
     }
 
     /** A builder of {@link TabTemplate}. */
@@ -180,10 +194,13 @@
         @Nullable
         Action mHeaderAction;
 
-        final List<Tab> mTabs = new ArrayList<>();
+        final List<Tab> mTabs;
         @Nullable
         TabContents mTabContents;
 
+        @Nullable
+        String mActiveTabContentId;
+
         /**
          * Sets whether the template is in a loading state.
          *
@@ -234,6 +251,19 @@
         }
 
         /**
+         * Stores the given {@code contentId} as the "active tab" to show on the screen. The given
+         * ID must match a tab that was added by {@link #addTab(Tab)}.
+         */
+        @NonNull
+        public TabTemplate.Builder setActiveTabContentId(@NonNull String contentId) {
+            if (requireNonNull(contentId).isEmpty()) {
+                throw new IllegalArgumentException("The content ID cannot be null or empty");
+            }
+            mActiveTabContentId = contentId;
+            return this;
+        }
+
+        /**
          * Adds an {@link Tab} to display in the template.
          *
          * @throws NullPointerException if {@code tab} is {@code null}
@@ -274,8 +304,14 @@
                                 + "contents");
             }
 
-            if (hasTabs) {
-                TabsConstraints.DEFAULT.validateOrThrow(mTabs);
+            if (hasTabs && mActiveTabContentId == null) {
+                throw new IllegalStateException(
+                        "Template requires setting content ID for the active tab when not in "
+                                + "Loading state");
+            }
+
+            if (hasTabs && mActiveTabContentId != null) {
+                TabsConstraints.DEFAULT.validateOrThrow(mTabs, mActiveTabContentId);
             }
 
             if (!mIsLoading && mHeaderAction == null) {
@@ -292,6 +328,17 @@
         @SuppressLint("ExecutorRegistration")
         public Builder(@NonNull TabCallback callback) {
             mTabCallbackDelegate = TabCallbackDelegateImpl.create(requireNonNull(callback));
+            mTabs = new ArrayList<>();
+        }
+
+        /** Creates a new {@link Builder}, populated from the input {@link TabTemplate} */
+        public Builder(@NonNull TabTemplate tabTemplate) {
+            mIsLoading = tabTemplate.isLoading();
+            mHeaderAction = tabTemplate.getHeaderAction();
+            mTabs = new ArrayList<>(tabTemplate.getTabs());
+            mTabContents = tabTemplate.getTabContents();
+            mTabCallbackDelegate = tabTemplate.getTabCallbackDelegate();
+            mActiveTabContentId = tabTemplate.getActiveTabContentId();
         }
     }
 }
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 9b089a6..d80ab48 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
@@ -22,7 +22,9 @@
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.Tab;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Encapsulates the constraints to apply when creating {@link TabTemplate}.
@@ -51,29 +53,36 @@
      *
      * @throws IllegalArgumentException if the constraints are not met
      */
-    public void validateOrThrow(@NonNull List<Tab> tabs) {
+    public void validateOrThrow(@NonNull List<Tab> tabs, @NonNull String activeTabContentId) {
         if (tabs.size() < mMinTabs) {
             throw new IllegalArgumentException(
-                    "Number of tabs set do not meet the minimum requirement of " + mMinTabs
-                            + " tabs");
+                    "There must be at least " + mMinTabs + " tab(s) added, but only found "
+                            + tabs.size());
         }
 
         if (tabs.size() > mMaxTabs) {
             throw new IllegalArgumentException(
-                    "Number of tabs set exceed the maximum allowed size of " + mMaxTabs);
+                    "There cannot be more than " + mMaxTabs + " tabs added, found " + tabs.size());
         }
 
-        int numOfActiveTabs = 0;
+        boolean hasTabWithActiveTabContentId = false;
+        Set<String> contentIdSet = new HashSet<>();
         for (Tab tab : tabs) {
-            if (tab.isActive()) {
-                numOfActiveTabs++;
+            if (activeTabContentId.equals(tab.getContentId())) {
+                hasTabWithActiveTabContentId = true;
+            }
+            if (!contentIdSet.add(tab.getContentId())) {
+                throw new IllegalArgumentException(
+                        "Found duplicate tab ID: " + tab.getContentId() + ". Each tab must have a"
+                                + " unique ID."
+                );
             }
         }
-        if (numOfActiveTabs == 0) {
-            throw new IllegalArgumentException("An active tab is required");
-        }
-        if (numOfActiveTabs > 1) {
-            throw new IllegalArgumentException("Only one active tab is allowed");
+
+        if (!hasTabWithActiveTabContentId) {
+            throw new IllegalArgumentException(
+                    "There is no tab with content ID matching the active tab content ID set on "
+                            + "the template");
         }
     }
 
diff --git a/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
index 9dc5d83..bafe3a7 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
@@ -240,6 +240,23 @@
     }
 
     @Test
+    public void createInstance_defaultItemImageShape() {
+        ItemList list = TestUtils.getGridItemList(2);
+        GridTemplate template = new GridTemplate.Builder().setSingleList(list).build();
+        assertThat(template.getItemImageShape()).isEqualTo(GridTemplate.ITEM_IMAGE_SHAPE_UNSET);
+    }
+
+    @Test
+    public void createInstance_setItemImageShape() {
+        ItemList list = TestUtils.getGridItemList(2);
+        GridTemplate template =
+                new GridTemplate.Builder()
+                        .setSingleList(list)
+                        .setItemImageShape(GridTemplate.ITEM_IMAGE_SHAPE_CIRCLE).build();
+        assertThat(template.getItemImageShape()).isEqualTo(GridTemplate.ITEM_IMAGE_SHAPE_CIRCLE);
+    }
+
+    @Test
     public void equals() {
         ItemList itemList = new ItemList.Builder().build();
         String title = "title";
@@ -366,7 +383,23 @@
                         .setLoading(true)
                         .setItemSize(GridTemplate.ITEM_SIZE_SMALL)
                         .build();
+        assertThat(template1).isNotEqualTo(template2);
+    }
 
+    @Test
+    public void notEquals_differentItemImageShape() {
+        ItemList itemList = new ItemList.Builder().build();
+
+        GridTemplate template1 =
+                new GridTemplate.Builder()
+                        .setSingleList(itemList)
+                        .setItemImageShape(GridTemplate.ITEM_IMAGE_SHAPE_CIRCLE)
+                        .build();
+        GridTemplate template2 =
+                new GridTemplate.Builder()
+                        .setSingleList(itemList)
+                        .setItemImageShape(GridTemplate.ITEM_IMAGE_SHAPE_UNSET)
+                        .build();
         assertThat(template1).isNotEqualTo(template2);
     }
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/TabTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/TabTemplateTest.java
index 8d1cbd8..046f416 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/TabTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/TabTemplateTest.java
@@ -52,6 +52,8 @@
                             .build())
             .build();
 
+    private static final String ACTIVE_TAB_CONTENT_ID = "ID_ACTIVE";
+
     @Test
     public void createInstance_emptyTemplate_notLoading_Throws() {
         assertThrows(
@@ -69,8 +71,9 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setLoading(true)
-                                .addTab(getTab("TAB_1", true))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -81,7 +84,21 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_1", true))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
+                                .build());
+    }
+
+    @Test
+    public void createInstance_activeTabContentIdNotSet_Throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () ->
+                        new TabTemplate.Builder(mMockTabCallback)
+                                .setHeaderAction(Action.APP_ICON)
+                                .addTab(getTab("TAB_1", "ID_1"))
+                                .addTab(getTab("TAB_2", "ID_2"))
                                 .setTabContents(TAB_CONTENTS)
                                 .build());
     }
@@ -93,9 +110,24 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_1", true))
-                                .addTab(getTab("TAB_2", true))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
+                                .build());
+    }
+
+    @Test
+    public void createInstance_noActiveTab_Throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new TabTemplate.Builder(mMockTabCallback)
+                                .setHeaderAction(Action.APP_ICON)
+                                .addTab(getTab("TAB_1", "ID_1"))
+                                .addTab(getTab("TAB_2", "ID_2"))
+                                .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -106,12 +138,30 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_1", true))
-                                .addTab(getTab("TAB_2", false))
-                                .addTab(getTab("TAB_3", false))
-                                .addTab(getTab("TAB_4", false))
-                                .addTab(getTab("TAB_5", false))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", "ID_2"))
+                                .addTab(getTab("TAB_3", "ID_3"))
+                                .addTab(getTab("TAB_4", "ID_4"))
+                                .addTab(getTab("TAB_5", "ID_5"))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
+                                .build());
+    }
+
+    @Test
+    public void createInstance_multipleTabsWithSameContentId_Throws() {
+        String duplicateId = "ID_DUPLICATE";
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new TabTemplate.Builder(mMockTabCallback)
+                                .setHeaderAction(Action.APP_ICON)
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", duplicateId))
+                                .addTab(getTab("TAB_3", duplicateId))
+                                .addTab(getTab("TAB_4", "ID_4"))
+                                .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -122,9 +172,10 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.BACK)
-                                .addTab(getTab("TAB_1", true))
-                                .addTab(getTab("TAB_2", false))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", "ID_2"))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -135,8 +186,9 @@
                 () ->
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_1", true))
-                                .addTab(getTab("TAB_2", false))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", "ID_2"))
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -144,16 +196,18 @@
     public void equals() {
         TabTemplate template1 = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         TabTemplate template2 = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertEquals(template1, template2);
@@ -163,18 +217,20 @@
     public void notEquals_differentTabs() {
         TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", "ID_1"))
+                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertThat(template)
                 .isNotEqualTo(
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_2", true))
-                                .addTab(getTab("TAB_3", false))
+                                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_3", "ID_3"))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -182,19 +238,21 @@
     public void notEquals_differentNumberOfTabs() {
         TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
-                .addTab(getTab("TAB_3", false))
+                .addTab(getTab("TAB_1", "ID_1"))
+                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_3", "ID_3"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertThat(template)
                 .isNotEqualTo(
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_2", true))
-                                .addTab(getTab("TAB_3", false))
+                                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_3", "ID_3"))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -202,16 +260,18 @@
     public void notEquals_differentActiveTab() {
         TabTemplate template1 = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         TabTemplate template2 = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", false))
-                .addTab(getTab("TAB_2", true))
+                .addTab(getTab("TAB_1", "ID_1"))
+                .addTab(getTab("TAB_2", ACTIVE_TAB_CONTENT_ID))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertNotEquals(template1, template2);
@@ -226,18 +286,20 @@
 
         TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
                 .setTabContents(new TabContents.Builder(listTemplate).build())
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertThat(template)
                 .isNotEqualTo(
                         new TabTemplate.Builder(mMockTabCallback)
                                 .setHeaderAction(Action.APP_ICON)
-                                .addTab(getTab("TAB_1", true))
-                                .addTab(getTab("TAB_2", false))
+                                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                                .addTab(getTab("TAB_2", "ID_2"))
                                 .setTabContents(TAB_CONTENTS)
+                                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                                 .build());
     }
 
@@ -245,9 +307,10 @@
     public void createInstance_twoTabs_valid() {
         TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertEquals(template.getTabs().size(), 2);
@@ -257,23 +320,57 @@
     public void createInstance_fourTabs_valid() {
         TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
                 .setHeaderAction(Action.APP_ICON)
-                .addTab(getTab("TAB_1", true))
-                .addTab(getTab("TAB_2", false))
-                .addTab(getTab("TAB_3", false))
-                .addTab(getTab("TAB_4", false))
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
+                .addTab(getTab("TAB_3", "ID_3"))
+                .addTab(getTab("TAB_4", "ID_4"))
                 .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
                 .build();
 
         assertEquals(template.getTabs().size(), 4);
     }
 
-    private static Tab getTab(String title, boolean isActive) {
+    @Test
+    public void copy_createsEquivalentInstance() {
+        TabTemplate template1 = new TabTemplate.Builder(mMockTabCallback)
+                .setHeaderAction(Action.APP_ICON)
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
+                .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
+                .build();
+
+        TabTemplate template2 = new TabTemplate.Builder(template1).build();
+
+        assertEquals(template1, template2);
+    }
+
+    @Test
+    public void copy_fieldsCanBeOverwritten() {
+        TabTemplate template = new TabTemplate.Builder(mMockTabCallback)
+                .setHeaderAction(Action.APP_ICON)
+                .addTab(getTab("TAB_1", ACTIVE_TAB_CONTENT_ID))
+                .addTab(getTab("TAB_2", "ID_2"))
+                .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId(ACTIVE_TAB_CONTENT_ID)
+                .build();
+
+        // Verify fields can be overwritten (no crash)
+        new TabTemplate.Builder(template)
+                .setHeaderAction(Action.APP_ICON)
+                .addTab(getTab("TAB_3", "ID_3"))
+                .setTabContents(TAB_CONTENTS)
+                .setActiveTabContentId("ID_3")
+                .build();
+    }
+
+    private static Tab getTab(String title, String contentId) {
         return new Tab.Builder()
-                .setContentId(title)
+                .setContentId(contentId)
                 .setIcon(TestUtils.getTestCarIcon(
                         ApplicationProvider.getApplicationContext(),
                         "ic_test_1"))
-                .setActive(isActive)
                 .setTitle(title)
                 .build();
     }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/TabTest.java b/car/app/app/src/test/java/androidx/car/app/model/TabTest.java
index 702c572..14a5e80 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/TabTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/TabTest.java
@@ -17,7 +17,6 @@
 package androidx.car.app.model;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThrows;
 
@@ -92,7 +91,7 @@
                 .setContentId("id")
                 .build();
 
-        assertFalse(tab.isActive());
+        assertEquals(tab.getContentId(), "id");
     }
 
     @Test
@@ -103,7 +102,6 @@
                         ApplicationProvider.getApplicationContext(),
                         "ic_test_1"))
                 .setContentId("id")
-                .setActive(false)
                 .build();
 
         assertEquals(tab, TEST_TAB);
@@ -140,11 +138,4 @@
 
         assertNotEquals(tab, TEST_TAB);
     }
-
-    @Test
-    public void notEquals_differentActiveState() {
-        Tab tab = TEST_TAB.toBuilder().setActive(true).build();
-
-        assertNotEquals(tab, TEST_TAB);
-    }
 }
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 315770c..647d7b5 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
@@ -124,7 +124,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.5.0-alpha03"
+        const val compilerVersion: String = "1.4.5"
         private val minimumRuntimeVersion: String
             get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
     }
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 320d61f..3954218 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -1296,7 +1296,7 @@
   }
 
   public final class BasicTextField2Kt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField2(androidx.compose.foundation.text2.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text2.input.TextEditFilter? filter, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text2.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.foundation.text2.input.CodepointTransformation? codepointTransformation, optional boolean secureContent, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField2(androidx.compose.foundation.text2.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text2.input.TextEditFilter? filter, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text2.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.foundation.text2.input.CodepointTransformation? codepointTransformation, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
 }
@@ -1446,8 +1446,8 @@
   @androidx.compose.foundation.ExperimentalFoundationApi public final class TextFieldState {
     ctor public TextFieldState(optional String initialText, optional long initialSelectionInChars);
     method public inline void edit(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text2.input.TextFieldBuffer,? extends androidx.compose.foundation.text2.input.TextEditResult> block);
-    method public androidx.compose.foundation.text2.input.TextFieldCharSequence getValue();
-    property public final androidx.compose.foundation.text2.input.TextFieldCharSequence value;
+    method public androidx.compose.foundation.text2.input.TextFieldCharSequence getText();
+    property public final androidx.compose.foundation.text2.input.TextFieldCharSequence text;
   }
 
   public static final class TextFieldState.Saver implements androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.text2.input.TextFieldState,java.lang.Object> {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
index 88597a5..6b7c425 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
@@ -49,7 +49,7 @@
         textStyle = LocalTextStyle.current,
         decorationBox = @Composable {
             TextFieldDefaults.OutlinedTextFieldDecorationBox(
-                value = state.value.toString(),
+                value = state.text.toString(),
                 visualTransformation = VisualTransformation.None,
                 innerTextField = it,
                 placeholder = null,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
index 3c6d1c3..fe5e726 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
@@ -31,7 +31,10 @@
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTextInput
@@ -59,6 +62,18 @@
     private val Tag = "BasicSecureTextField"
 
     @Test
+    fun passwordSemanticsAreSet() {
+        rule.setContent {
+            BasicSecureTextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.Password))
+    }
+
+    @Test
     fun lastTypedCharacterIsRevealedTemporarily() {
         rule.setContent {
             BasicSecureTextField(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
index 061247d..2761b7e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
@@ -228,7 +228,7 @@
         rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(2))
 
         rule.runOnIdle {
-            assertThat(state.value.selectionInChars).isEqualTo(TextRange(2))
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
         }
     }
 
@@ -290,20 +290,6 @@
         }
     }
 
-    @Test
-    fun passwordSemanticsAreSet_accordingToSecureContent() {
-        val state = TextFieldState()
-        rule.setContent {
-            BasicTextField2(
-                state = state,
-                secureContent = true,
-                modifier = Modifier.testTag(Tag)
-            )
-        }
-
-        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.Password))
-    }
-
     private fun SemanticsNodeInteraction.assertSelection(expected: TextRange) {
         val selection = fetchSemanticsNode().config
             .getOrNull(SemanticsProperties.TextSelectionRange)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
index 9d4250e..9f94b64 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
@@ -20,16 +20,21 @@
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.text.KeyboardHelper
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text2.input.TextEditFilter
 import androidx.compose.foundation.text2.input.TextFieldBuffer.ChangeList
 import androidx.compose.foundation.text2.input.TextFieldBufferWithSelection
-import androidx.compose.foundation.text2.input.TextEditFilter
 import androidx.compose.foundation.text2.input.TextFieldCharSequence
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.text2.input.internal.AndroidTextInputAdapter
 import androidx.compose.foundation.text2.input.rememberTextFieldState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -55,7 +60,10 @@
 import androidx.compose.ui.test.performTextInput
 import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.test.performTextReplacement
+import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
@@ -63,6 +71,7 @@
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardCapitalization
 import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -120,7 +129,7 @@
         rule.onNodeWithTag(Tag).performTextInput("World!")
 
         rule.runOnIdle {
-            assertThat(state.value.toString()).isEqualTo("Hello World!")
+            assertThat(state.text.toString()).isEqualTo("Hello World!")
         }
 
         rule.onNodeWithTag(Tag).assertTextEquals("Hello World!")
@@ -329,8 +338,8 @@
             performTextReplacement("Compose2")
             assertTextEquals("Compose2")
         }
-        assertThat(state1.value.toString()).isEqualTo("Compose")
-        assertThat(state2.value.toString()).isEqualTo("Compose2")
+        assertThat(state1.text.toString()).isEqualTo("Compose")
+        assertThat(state2.text.toString()).isEqualTo("Compose2")
     }
 
     @Test
@@ -865,6 +874,72 @@
         }
     }
 
+    @Test
+    fun textField_showsKeyboardAgainWhenTapped_ifFocused() {
+        val keyboardHelper = KeyboardHelper(rule)
+        rule.setContent {
+            keyboardHelper.initialize()
+            BasicTextField2(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        // make sure keyboard is hidden initially
+        keyboardHelper.hideKeyboardIfShown()
+
+        // click the first time to gain focus.
+        rule.onNodeWithTag(Tag).performClick()
+        keyboardHelper.waitForKeyboardVisibility(true)
+        assertThat(keyboardHelper.isSoftwareKeyboardShown()).isTrue()
+
+        // hide it again.
+        keyboardHelper.hideKeyboardIfShown()
+        rule.onNodeWithTag(Tag).assertIsFocused()
+        rule.onNodeWithTag(Tag).performClick()
+
+        // expect keyboard to show up again.
+        keyboardHelper.waitForKeyboardVisibility(true)
+        assertThat(keyboardHelper.isSoftwareKeyboardShown()).isTrue()
+    }
+
+    @Test
+    fun swipingThroughTextField_doesNotGainFocus() {
+        rule.setContent {
+            BasicTextField2(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).performTouchInput {
+            // swipe through
+            swipeRight(endX = right + 200, durationMillis = 1000)
+        }
+        rule.onNodeWithTag(Tag).assertIsNotFocused()
+    }
+
+    @Test
+    fun swipingTextFieldInScrollableContainer_doesNotGainFocus() {
+        val scrollState = ScrollState(0)
+        rule.setContent {
+            Column(Modifier.size(100.dp).verticalScroll(scrollState)) {
+                BasicTextField2(
+                    state = rememberTextFieldState(),
+                    modifier = Modifier.testTag(Tag)
+                )
+                Box(Modifier.size(200.dp))
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTouchInput {
+            // swipe through
+            swipeUp(durationMillis = 1000)
+        }
+        rule.onNodeWithTag(Tag).assertIsNotFocused()
+        assertThat(scrollState.value).isNotEqualTo(0)
+    }
+
     private fun requestFocus(tag: String) =
         rule.onNodeWithTag(tag).performSemanticsAction(SemanticsActions.RequestFocus)
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt
index 148087d..673dd9e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt
@@ -173,7 +173,7 @@
         }
 
         rule.runOnIdle {
-            assertThat(state.value.toString()).isEqualTo("hello")
+            assertThat(state.text.toString()).isEqualTo("hello")
         }
     }
 
@@ -208,7 +208,7 @@
         }
 
         rule.runOnIdle {
-            assertThat(state.value.toString()).isEqualTo("hello")
+            assertThat(state.text.toString()).isEqualTo("hello")
         }
     }
 
@@ -242,7 +242,7 @@
 
         // assertThat selection happened
         rule.runOnIdle {
-            assertThat(state.value.selectionInChars).isEqualTo(TextRange(0, 5))
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 5))
         }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
index 2aa87be..b615766 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
@@ -108,7 +108,7 @@
     private var textLayoutResult: TextLayoutResult? = null
     private val cursorRect: Rect
         // assume selection is collapsed
-        get() = textLayoutResult?.getCursorRect(state.value.selectionInChars.start) ?: Rect.Zero
+        get() = textLayoutResult?.getCursorRect(state.text.selectionInChars.start) ?: Rect.Zero
 
     private val backgroundModifier = Modifier.background(contentColor)
     private val focusModifier = Modifier.onFocusChanged { if (it.isFocused) isFocused = true }
@@ -575,7 +575,7 @@
                     textStyle = textStyle,
                     modifier = textFieldModifier.layout { measurable, constraints ->
                         // change the state during layout so draw can read the new state
-                        val currValue = state.value
+                        val currValue = state.text
                         if (currValue.isNotEmpty()) {
                             val newText = currValue.dropLast(1)
                             val newValue =
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt
index fed092c..8c399e13 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt
@@ -623,13 +623,13 @@
 
         fun expectedText(text: String) {
             rule.runOnIdle {
-                assertThat(state.value.toString()).isEqualTo(text)
+                assertThat(state.text.toString()).isEqualTo(text)
             }
         }
 
         fun expectedSelection(selection: TextRange) {
             rule.runOnIdle {
-                assertThat(state.value.selectionInChars).isEqualTo(selection)
+                assertThat(state.text.selectionInChars).isEqualTo(selection)
             }
         }
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldScrollTest.kt
index 7661d74..5285a66 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldScrollTest.kt
@@ -401,7 +401,7 @@
         // move cursor to the end
         // TODO
         state.editProcessor.reset(
-            TextFieldCharSequence(state.value, selection = TextRange(longText.length))
+            TextFieldCharSequence(state.text, selection = TextRange(longText.length))
         )
 
         rule.runOnIdle {
@@ -426,7 +426,7 @@
 
         // move cursor to the end
         state.editProcessor.reset(
-            TextFieldCharSequence(state.value, selection = TextRange(longText.length))
+            TextFieldCharSequence(state.text, selection = TextRange(longText.length))
         )
 
         rule.runOnIdle {
@@ -454,7 +454,7 @@
 
         rule.runOnIdle {
             assertThat(scrollState.value).isEqualTo(scrollState.maxValue)
-            assertThat(state.value.selectionInChars).isEqualTo(TextRange(5))
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldStateRestorationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldStateRestorationTest.kt
index 62a1def..cae64e7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldStateRestorationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldStateRestorationTest.kt
@@ -64,8 +64,8 @@
         restorationTester.emulateSavedInstanceStateRestore()
 
         rule.runOnIdle {
-            assertThat(restoredState.value.toString()).isEqualTo("hello, world")
-            assertThat(restoredState.value.selectionInChars).isEqualTo(TextRange(0, 12))
+            assertThat(restoredState.text.toString()).isEqualTo("hello, world")
+            assertThat(restoredState.text.selectionInChars).isEqualTo(TextRange(0, 12))
         }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapterTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapterTest.kt
index 5b75315..2c65d65 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapterTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapterTest.kt
@@ -147,8 +147,8 @@
 
             connection.commitText("Hello", 0)
 
-            assertThat(state1.value.toString()).isEqualTo("")
-            assertThat(state2.value.toString()).isEqualTo("Hello")
+            assertThat(state1.text.toString()).isEqualTo("")
+            assertThat(state2.text.toString()).isEqualTo("Hello")
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
index 75cabf5..2b9bd1c 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
@@ -43,6 +43,8 @@
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.semantics.password
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
@@ -159,11 +161,19 @@
         }
     }
 
+    val secureTextFieldModifier = modifier
+        .semantics(mergeDescendants = true) { password() }
+        .then(
+            if (revealLastTypedEnabled) {
+                secureTextFieldController.focusChangeModifier
+            } else {
+                Modifier
+            }
+        )
+
     BasicTextField2(
         state = state,
-        modifier = if (revealLastTypedEnabled) {
-            modifier.then(secureTextFieldController.focusChangeModifier)
-        } else modifier,
+        modifier = secureTextFieldModifier,
         enabled = enabled,
         readOnly = false,
         filter = if (revealLastTypedEnabled) {
@@ -185,7 +195,6 @@
         onTextLayout = onTextLayout,
         codepointTransformation = codepointTransformation,
         decorationBox = decorationBox,
-        secureContent = true
     )
 }
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
index de7736c..b51170c 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
@@ -108,11 +108,6 @@
  * scroll behavior. In other cases the text field becomes vertically scrollable.
  * @param codepointTransformation Visual transformation interface that provides a 1-to-1 mapping of
  * codepoints.
- * @param secureContent Controls whether the content of this editor should be secured, for example,
- * if it's a password input field. Enabling this flag will disable context menu actions like cut,
- * copy, and paste for added security. It will also notify the accessibility service that the
- * editor may contain sensitive input. However, just turning on this flag is not enough to use this
- * editor as a password field. For password entry fields, please refer to [BasicSecureTextField].
  * @param decorationBox Composable lambda that allows to add decorations around text field, such
  * as icon, placeholder, helper messages or similar, and automatically increase the hit target area
  * of the text field. To allow you to control the placement of the inner text field relative to your
@@ -138,7 +133,6 @@
     cursorBrush: Brush = SolidColor(Color.Black),
     scrollState: ScrollState = rememberScrollState(),
     codepointTransformation: CodepointTransformation? = null,
-    secureContent: Boolean = false,
     decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
         @Composable { innerTextField -> innerTextField() }
 ) {
@@ -159,7 +153,7 @@
     val textLayoutState = remember {
         TextLayoutState(
             TextDelegate(
-                text = AnnotatedString(state.value.toString()),
+                text = AnnotatedString(state.text.toString()),
                 style = textStyle,
                 density = density,
                 fontFamilyResolver = fontFamilyResolver,
@@ -182,7 +176,6 @@
                 keyboardOptions = keyboardOptions,
                 keyboardActions = keyboardActions,
                 singleLine = singleLine,
-                secureContent = secureContent
             )
         )
         .focusable(interactionSource = interactionSource, enabled = enabled)
@@ -232,7 +225,7 @@
 
             Layout(modifier = coreModifiers) { _, constraints ->
                 val result = with(textLayoutState) {
-                    val visualText = state.value.toVisualText(codepointTransformation)
+                    val visualText = state.text.toVisualText(codepointTransformation)
                     layout(
                         text = AnnotatedString(visualText.toString()),
                         textStyle = textStyle,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/TextFieldState.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/TextFieldState.kt
index ef350fa..9cd5e6a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/TextFieldState.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/TextFieldState.kt
@@ -40,7 +40,7 @@
     internal var editProcessor =
         EditProcessor(TextFieldCharSequence(initialText, initialSelectionInChars))
 
-    val value: TextFieldCharSequence
+    val text: TextFieldCharSequence
         get() = editProcessor.value
 
     /**
@@ -54,13 +54,13 @@
      * @see setTextAndSelectAll
      */
     inline fun edit(block: TextFieldBuffer.() -> TextEditResult) {
-        val mutableValue = startEdit(value)
+        val mutableValue = startEdit(text)
         val result = mutableValue.block()
         commitEdit(mutableValue, result)
     }
 
     override fun toString(): String =
-        "TextFieldState(selection=${value.selectionInChars}, text=\"$value\")"
+        "TextFieldState(selection=${text.selectionInChars}, text=\"$text\")"
 
     @Suppress("ShowingMemberInHiddenClass")
     @PublishedApi
@@ -70,7 +70,7 @@
     @Suppress("ShowingMemberInHiddenClass")
     @PublishedApi
     internal fun commitEdit(newValue: TextFieldBuffer, result: TextEditResult) {
-        val newSelection = result.calculateSelection(value, newValue)
+        val newSelection = result.calculateSelection(text, newValue)
         val finalValue = newValue.toTextFieldCharSequence(newSelection)
         editProcessor.reset(finalValue)
     }
@@ -84,9 +84,9 @@
     @Suppress("RedundantNullableReturnType")
     object Saver : androidx.compose.runtime.saveable.Saver<TextFieldState, Any> {
         override fun SaverScope.save(value: TextFieldState): Any? = listOf(
-            value.value.toString(),
-            value.value.selectionInChars.start,
-            value.value.selectionInChars.end
+            value.text.toString(),
+            value.text.selectionInChars.start,
+            value.text.selectionInChars.end
         )
 
         override fun restore(value: Any): TextFieldState? {
@@ -146,7 +146,7 @@
 
 @OptIn(ExperimentalFoundationApi::class)
 internal fun TextFieldState.deselect() {
-    if (!value.selectionInChars.collapsed) {
+    if (!text.selectionInChars.collapsed) {
         edit {
             selectCharsIn(TextRange.Zero)
         }
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 d04a716..1877cfa 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
@@ -184,7 +184,7 @@
 
         // region EditableTextInputSession
         override val value: TextFieldCharSequence
-            get() = state.value
+            get() = state.text
 
         private var filter: TextEditFilter? = initialFilter
 
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 ffe9ad8..655d496 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
@@ -181,7 +181,7 @@
             changeObserverJob = coroutineScope.launch {
                 // Animate the cursor even when animations are disabled by the system.
                 withContext(FixedMotionDurationScale) {
-                    snapshotFlow { textFieldState.value }
+                    snapshotFlow { textFieldState.text }
                         .collectLatest {
                             // ensure that the value is always 1f _this_ frame by calling snapTo
                             cursorAlpha.snapTo(1f)
@@ -204,7 +204,7 @@
 
     override fun ContentDrawScope.draw() {
         drawContent()
-        val value = textFieldState.value
+        val value = textFieldState.text
         val textLayoutResult = textLayoutState.layoutResult ?: return
 
         if (value.selectionInChars.collapsed) {
@@ -224,7 +224,7 @@
         measurable: Measurable,
         constraints: Constraints
     ): MeasureResult {
-        val currSelection = textFieldState.value.selectionInChars
+        val currSelection = textFieldState.text.selectionInChars
         val offsetToFollow = when {
             currSelection.start != previousSelection.start -> currSelection.start
             currSelection.end != previousSelection.end -> currSelection.end
@@ -260,7 +260,7 @@
         measurable: Measurable,
         constraints: Constraints
     ): MeasureResult {
-        val value = textFieldState.value
+        val value = textFieldState.text
         val offsetToFollow = when {
             value.selectionInChars.start != previousSelection.start -> value.selectionInChars.start
             value.selectionInChars.end != previousSelection.end -> value.selectionInChars.end
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 38f88b4..b1955d7 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
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.text2.input.internal
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.detectTapAndPress
 import androidx.compose.foundation.text.KeyboardActionScope
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
@@ -25,7 +26,6 @@
 import androidx.compose.foundation.text2.input.TextFieldCharSequence
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.text2.input.deselect
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusEventModifierNode
 import androidx.compose.ui.focus.FocusManager
@@ -36,9 +36,10 @@
 import androidx.compose.ui.input.key.KeyInputModifierNode
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.changedToDown
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.GlobalPositionAwareModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.PointerInputModifierNode
@@ -53,7 +54,6 @@
 import androidx.compose.ui.semantics.imeAction
 import androidx.compose.ui.semantics.insertTextAtCursor
 import androidx.compose.ui.semantics.onClick
-import androidx.compose.ui.semantics.password
 import androidx.compose.ui.semantics.performImeAction
 import androidx.compose.ui.semantics.setSelection
 import androidx.compose.ui.semantics.setText
@@ -64,7 +64,6 @@
 import androidx.compose.ui.text.input.KeyboardCapitalization
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.util.fastAny
 
 /**
  * Modifier element for most of the functionality of [BasicTextField2] that is attached to the
@@ -84,7 +83,6 @@
     private val keyboardOptions: KeyboardOptions,
     private val keyboardActions: KeyboardActions,
     private val singleLine: Boolean,
-    private val secureContent: Boolean
 ) : ModifierNodeElement<TextFieldDecoratorModifierNode>() {
     override fun create(): TextFieldDecoratorModifierNode = TextFieldDecoratorModifierNode(
         textFieldState = textFieldState,
@@ -96,7 +94,6 @@
         keyboardOptions = keyboardOptions,
         keyboardActions = keyboardActions,
         singleLine = singleLine,
-        secureContent = secureContent,
     )
 
     override fun update(node: TextFieldDecoratorModifierNode): TextFieldDecoratorModifierNode {
@@ -110,7 +107,6 @@
             keyboardOptions = keyboardOptions,
             keyboardActions = keyboardActions,
             singleLine = singleLine,
-            secureContent = secureContent,
         )
         return node
     }
@@ -132,8 +128,7 @@
     keyboardOptions: KeyboardOptions,
     var keyboardActions: KeyboardActions,
     var singleLine: Boolean,
-    var secureContent: Boolean,
-) : Modifier.Node(),
+) : DelegatingNode(),
     SemanticsModifierNode,
     FocusRequesterModifierNode,
     FocusEventModifierNode,
@@ -142,6 +137,19 @@
     KeyInputModifierNode,
     CompositionLocalConsumerModifierNode {
 
+    private val pointerInputNode = SuspendingPointerInputModifierNode {
+        detectTapAndPress(onTap = {
+            if (!isFocused) {
+                requestFocus()
+            } else if (enabled && !readOnly) {
+                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
 
@@ -149,7 +157,6 @@
     private var lastText: CharSequence? = null
     private var lastSelection: TextRange? = null
     private var lastEnabled: Boolean = enabled
-    private var lastSecureContent: Boolean = secureContent
 
     private var isFocused: Boolean = false
     private var semanticsConfigurationCache: SemanticsConfiguration? = null
@@ -212,7 +219,6 @@
         keyboardOptions: KeyboardOptions,
         keyboardActions: KeyboardActions,
         singleLine: Boolean,
-        secureContent: Boolean,
     ) {
         // Find the diff: current previous and new values before updating current.
         val previousWriteable = this.enabled && !this.readOnly
@@ -230,7 +236,6 @@
         this.keyboardOptions = keyboardOptions.withDefaultsFrom(filter?.keyboardOptions)
         this.keyboardActions = keyboardActions
         this.singleLine = singleLine
-        this.secureContent = secureContent
 
         // React to diff.
         // If made writable while focused, or we got a completely new state instance,
@@ -263,14 +268,13 @@
     override val semanticsConfiguration: SemanticsConfiguration
         get() {
             var localSemantics = semanticsConfigurationCache
-            val value = textFieldState.value
+            val value = textFieldState.text
             // Cache invalidation is done here instead of only in updateNode because the text or
             // selection might change without triggering a modifier update.
             if (localSemantics == null ||
                 !value.contentEquals(lastText) ||
                 lastSelection != value.selectionInChars ||
-                lastEnabled != enabled ||
-                lastSecureContent != secureContent
+                lastEnabled != enabled
             ) {
                 localSemantics = generateSemantics(value, value.selectionInChars)
             }
@@ -311,13 +315,11 @@
         pass: PointerEventPass,
         bounds: IntSize
     ) {
-        if (pass == PointerEventPass.Main && pointerEvent.changes.fastAny { it.changedToDown() }) {
-            requestFocus()
-        }
+        pointerInputNode.onPointerEvent(pointerEvent, pass, bounds)
     }
 
     override fun onCancelPointerInput() {
-        // Nothing to do yet, since onPointerEvent isn't handling any gestures.
+        pointerInputNode.onCancelPointerInput()
     }
 
     override fun onPreKeyEvent(event: KeyEvent): Boolean {
@@ -352,7 +354,6 @@
             textSelectionRange = selection
             imeAction = keyboardOptions.imeAction
             if (!enabled) disabled()
-            if (secureContent) password()
 
             setText { text ->
                 textFieldState.editProcessor.update(
@@ -379,7 +380,7 @@
                     // reset is required to make sure IME gets the update.
                     textFieldState.editProcessor.reset(
                         TextFieldCharSequence(
-                            text = textFieldState.value,
+                            text = textFieldState.text,
                             selection = TextRange(start, end)
                         )
                     )
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextPreparedSelection.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextPreparedSelection.kt
index 7c4310a..9bc6d85 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextPreparedSelection.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextPreparedSelection.kt
@@ -78,7 +78,7 @@
      * [TextFieldPreparedSelection]. It is also used to make comparison between the initial state
      * and the modified state of selection and content.
      */
-    val initialValue = Snapshot.withoutReadObservation { state.value }
+    val initialValue = Snapshot.withoutReadObservation { state.text }
 
     /**
      * Current active selection in the context of this [TextFieldPreparedSelection]
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateSaverTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateSaverTest.kt
index 792ccef..4325a6a 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateSaverTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateSaverTest.kt
@@ -35,8 +35,8 @@
         val restoredState = TextFieldState.Saver.restore(saved)
 
         assertNotNull(restoredState)
-        assertThat(restoredState.value.toString()).isEqualTo("hello, world")
-        assertThat(restoredState.value.selectionInChars).isEqualTo(TextRange(0, 5))
+        assertThat(restoredState.text.toString()).isEqualTo("hello, world")
+        assertThat(restoredState.text.selectionInChars).isEqualTo(TextRange(0, 5))
     }
 
     private object TestSaverScope : SaverScope {
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateTest.kt
index 8079447..d068e21 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text2/input/TextFieldStateTest.kt
@@ -32,7 +32,7 @@
 
     @Test
     fun initialValue() {
-        assertThat(state.value.toString()).isEqualTo("")
+        assertThat(state.text.toString()).isEqualTo("")
     }
 
     @Test
@@ -46,7 +46,7 @@
             }
         }
 
-        assertThat(state.value.toString()).isEmpty()
+        assertThat(state.text.toString()).isEmpty()
     }
 
     @Test
@@ -65,14 +65,14 @@
             replace(0, 0, "hello")
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("hello")
+        assertThat(state.text.toString()).isEqualTo("hello")
     }
 
     @Test
     fun edit_replace_doesNotChangeStateUntilReturn() {
         state.edit {
             replace(0, 0, "hello")
-            assertThat(state.value.toString()).isEmpty()
+            assertThat(state.text.toString()).isEmpty()
             placeCursorAtEnd()
         }
     }
@@ -85,10 +85,10 @@
             replace(5, 5, " ")
             replace(6, 11, "Compose")
             assertThat(toString()).isEqualTo("hello Compose")
-            assertThat(state.value.toString()).isEmpty()
+            assertThat(state.text.toString()).isEmpty()
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("hello Compose")
+        assertThat(state.text.toString()).isEqualTo("hello Compose")
     }
 
     @Test
@@ -97,7 +97,7 @@
             replace(0, 0, "hello")
             placeCursorAtEnd()
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(5))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
     }
 
     @Test
@@ -106,7 +106,7 @@
             replace(0, 0, "hello")
             placeCursorBeforeCharAt(2)
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(2))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
     }
 
     @Test
@@ -128,7 +128,7 @@
             replace(0, 0, "hello")
             placeCursorBeforeCodepointAt(2)
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(2))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
     }
 
     @Test
@@ -150,7 +150,7 @@
             replace(0, 0, "hello")
             selectAll()
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(0, 5))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 5))
     }
 
     @Test
@@ -159,7 +159,7 @@
             replace(0, 0, "hello")
             selectCharsIn(TextRange(1, 4))
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(1, 4))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(1, 4))
     }
 
     @Test
@@ -187,7 +187,7 @@
             replace(0, 0, "hello")
             selectCodepointsIn(TextRange(1, 4))
         }
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(1, 4))
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(1, 4))
     }
 
     @Test
@@ -221,7 +221,7 @@
             assertThat(toString()).isEqualTo("hello world")
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("hello world")
+        assertThat(state.text.toString()).isEqualTo("hello world")
     }
 
     @Test
@@ -230,7 +230,7 @@
             append('c')
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("c")
+        assertThat(state.text.toString()).isEqualTo("c")
     }
 
     @Test
@@ -239,7 +239,7 @@
             append("hello")
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("hello")
+        assertThat(state.text.toString()).isEqualTo("hello")
     }
 
     @Test
@@ -248,21 +248,21 @@
             append("hello world", 0, 5)
             placeCursorAtEnd()
         }
-        assertThat(state.value.toString()).isEqualTo("hello")
+        assertThat(state.text.toString()).isEqualTo("hello")
     }
 
     @Test
     fun setTextAndPlaceCursorAtEnd_works() {
         state.setTextAndPlaceCursorAtEnd("Hello")
-        assertThat(state.value.toString()).isEqualTo("Hello")
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(5))
+        assertThat(state.text.toString()).isEqualTo("Hello")
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
     }
 
     @Test
     fun setTextAndSelectAll_works() {
         state.setTextAndSelectAll("Hello")
-        assertThat(state.value.toString()).isEqualTo("Hello")
-        assertThat(state.value.selectionInChars).isEqualTo(TextRange(0, 5))
+        assertThat(state.text.toString()).isEqualTo("Hello")
+        assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 5))
     }
 
     @Test
diff --git a/compose/material3/material3/api/current.ignore b/compose/material3/material3/api/current.ignore
new file mode 100644
index 0000000..dd3f23a
--- /dev/null
+++ b/compose/material3/material3/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.material3.TimePickerState:
+    Removed class androidx.compose.material3.TimePickerState
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index a0df8a5..0901450 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -826,22 +826,6 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
-  @androidx.compose.runtime.Stable public final class TimePickerState {
-    ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
-    method public int getHour();
-    method public int getMinute();
-    method public boolean is24hour();
-    method public suspend Object? settle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    property public final int hour;
-    property public final boolean is24hour;
-    property public final int minute;
-    field public static final androidx.compose.material3.TimePickerState.Companion Companion;
-  }
-
-  public static final class TimePickerState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TimePickerState,?> Saver();
-  }
-
   @androidx.compose.runtime.Immutable public final class Typography {
     ctor public Typography(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 headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, 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 labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.material3.Typography copy(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 headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, 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 labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index fd0421f..c9a1a36 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -1229,7 +1229,7 @@
     property public final int Vertical;
   }
 
-  @androidx.compose.runtime.Stable public final class TimePickerState {
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TimePickerState {
     ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
     method public int getHour();
     method public int getMinute();
diff --git a/compose/material3/material3/api/restricted_current.ignore b/compose/material3/material3/api/restricted_current.ignore
new file mode 100644
index 0000000..dd3f23a
--- /dev/null
+++ b/compose/material3/material3/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.material3.TimePickerState:
+    Removed class androidx.compose.material3.TimePickerState
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index a0df8a5..0901450 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -826,22 +826,6 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
-  @androidx.compose.runtime.Stable public final class TimePickerState {
-    ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
-    method public int getHour();
-    method public int getMinute();
-    method public boolean is24hour();
-    method public suspend Object? settle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    property public final int hour;
-    property public final boolean is24hour;
-    property public final int minute;
-    field public static final androidx.compose.material3.TimePickerState.Companion Companion;
-  }
-
-  public static final class TimePickerState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TimePickerState,?> Saver();
-  }
-
   @androidx.compose.runtime.Immutable public final class Typography {
     ctor public Typography(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 headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, 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 labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.material3.Typography copy(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 headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, 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 labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
index 04a642d..cd8bc57 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
@@ -488,6 +488,7 @@
  *  or `true` for 24 hour format without toggle.
  */
 @Stable
+@ExperimentalMaterial3Api
 class TimePickerState(
     initialHour: Int,
     initialMinute: Int,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
index 5644c62..69e32d6 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
@@ -19,11 +19,11 @@
 import androidx.compose.runtime.identityHashCode
 
 internal class IdentityArrayIntMap {
-    internal var size = 0
+    var size = 0
         private set
-    internal var keys: Array<Any?> = arrayOfNulls(4)
+    var keys: Array<Any?> = arrayOfNulls(4)
         private set
-    internal var values: IntArray = IntArray(4)
+    var values: IntArray = IntArray(4)
         private set
 
     operator fun get(key: Any): Int {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
index cb6d619..bc600cc 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
@@ -19,11 +19,11 @@
 import androidx.compose.runtime.identityHashCode
 
 internal class IdentityArrayMap<Key : Any, Value : Any?>(capacity: Int = 16) {
-    internal var keys = arrayOfNulls<Any?>(capacity)
+    var keys = arrayOfNulls<Any?>(capacity)
         private set
-    internal var values = arrayOfNulls<Any?>(capacity)
+    var values = arrayOfNulls<Any?>(capacity)
         private set
-    internal var size = 0
+    var size = 0
         private set
 
     fun isEmpty() = size == 0
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
index 29721ca..1c250bd 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
@@ -29,8 +29,7 @@
     override var size = 0
         private set
 
-    @PublishedApi
-    internal var values: Array<Any?> = arrayOfNulls(16)
+    var values: Array<Any?> = arrayOfNulls(16)
         private set
 
     /**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
index f94b85f..d683a23 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
@@ -30,28 +30,27 @@
      * in the [IdentityScopeMap]. The length of the used values is [size], and all remaining values
      * are the unused indices in [values] and [scopeSets].
      */
-    @PublishedApi
-    internal var valueOrder: IntArray = IntArray(50) { it }
+    var valueOrder: IntArray = IntArray(50) { it }
+        private set
 
     /**
      * The [identityHashCode] for the keys in the collection. We never use the actual
      * values
      */
-    @PublishedApi
-    internal var values: Array<Any?> = arrayOfNulls(50)
+    var values: Array<Any?> = arrayOfNulls(50)
+        private set
 
     /**
      * The [IdentityArraySet]s for values, in the same index order as [values], indexed
      * by [valueOrder]. The consumed values may extend beyond [size] if a value has been removed.
      */
-    @PublishedApi
-    internal var scopeSets: Array<IdentityArraySet<T>?> = arrayOfNulls(50)
+    var scopeSets: Array<IdentityArraySet<T>?> = arrayOfNulls(50)
+        private set
 
     /**
      * The number of values in the map.
      */
-    @PublishedApi
-    internal var size = 0
+    var size = 0
 
     /**
      * Returns the [IdentityArraySet] for the value at the given [index] order in the map.
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 6ae25e4..6409344 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
@@ -18,7 +18,9 @@
 
 package androidx.compose.ui.modifier
 
+import androidx.compose.runtime.Applier
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.ReusableContent
 import androidx.compose.runtime.ReusableContentHost
 import androidx.compose.runtime.getValue
@@ -35,9 +37,11 @@
 import androidx.compose.ui.layout.MeasurePolicy
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.ComposeUiNode
 import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.ObserverNode
 import androidx.compose.ui.node.observeReads
@@ -49,6 +53,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -233,6 +238,54 @@
         }
     }
 
+    // Regression test for b/275919849
+    @Test
+    fun unchangedNodesAreDetachedAndReattachedWhenReused() {
+        val nodeInstance = object : Modifier.Node() {}
+        val element = object : ModifierNodeElement<Modifier.Node>() {
+            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
+        }
+
+        var active by mutableStateOf(true)
+        rule.setContent {
+            ReusableContentHost(active) {
+                // Custom Layout that measures to 1x1 pixels and only assigns the modifiers once
+                // when the node is created. Even if the modifiers aren't reassigned, they should
+                // still undergo the same lifecycle.
+                ReusableComposeNode<ComposeUiNode, Applier<Any>>(
+                    factory = {
+                        LayoutNode().apply {
+                            measurePolicy = MeasurePolicy { _, _ -> layout(1, 1) {} }
+                            modifier = element
+                        }
+                    },
+                    update = { },
+                    content = { }
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("Modifier Node was not attached when being initially created")
+                .that(nodeInstance.isAttached).isTrue()
+        }
+
+        active = false
+        rule.runOnIdle {
+            assertWithMessage("Modifier Node should be detached when its LayoutNode is deactivated")
+                .that(nodeInstance.isAttached).isFalse()
+        }
+
+        active = true
+        rule.runOnIdle {
+            assertWithMessage("Modifier Node was not attached after being reactivated")
+                .that(nodeInstance.isAttached).isTrue()
+        }
+    }
+
     @Test
     fun nodesAreDetachedAndAttachedWhenDeactivatedAndReactivated() {
         var active by mutableStateOf(true)
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 ee20288..17391e0 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
@@ -25,6 +25,7 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.SystemClock
+import android.text.SpannableString
 import android.util.Log
 import android.view.MotionEvent
 import android.view.View
@@ -723,6 +724,17 @@
         }
     }
 
+    private fun isScreenReaderFocusable(
+        node: SemanticsNode
+    ): Boolean {
+        val isSpeakingNode = node.infoContentDescriptionOrNull != null ||
+            getInfoText(node) != null || getInfoStateDescriptionOrNull(node) != null ||
+            getInfoIsCheckable(node)
+
+        return node.unmergedConfig.isMergingSemanticsOfDescendants ||
+            node.isUnmergedLeafNode && isSpeakingNode
+    }
+
     @VisibleForTesting
     @OptIn(ExperimentalComposeUiApi::class)
     fun populateAccessibilityNodeInfoProperties(
@@ -730,15 +742,6 @@
         info: AccessibilityNodeInfoCompat,
         semanticsNode: SemanticsNode
     ) {
-        val isUnmergedLeafNode =
-            !semanticsNode.isFake &&
-                semanticsNode.replacedChildren.isEmpty() &&
-                semanticsNode.layoutNode.findClosestParentNode {
-                    it.outerSemantics
-                        ?.collapsedSemanticsConfiguration()
-                        ?.isMergingSemanticsOfDescendants == true
-                } == null
-
         // set classname
         info.className = ClassName
         val role = semanticsNode.unmergedConfig.getOrNull(SemanticsProperties.Role)
@@ -753,7 +756,7 @@
                     // Images are often minor children of larger widgets, so we only want to
                     // announce the Image role when the image itself is focusable.
                     if (role != Role.Image ||
-                        isUnmergedLeafNode ||
+                        semanticsNode.isUnmergedLeafNode ||
                         semanticsNode.unmergedConfig.isMergingSemanticsOfDescendants
                     ) {
                         info.className = className
@@ -798,39 +801,17 @@
 
         setText(semanticsNode, info)
         setContentInvalid(semanticsNode, info)
+        setStateDescription(semanticsNode, info)
+        setIsCheckable(semanticsNode, info)
 
-        info.stateDescription =
-            semanticsNode.unmergedConfig.getOrNull(SemanticsProperties.StateDescription)
         val toggleState = semanticsNode.unmergedConfig.getOrNull(
             SemanticsProperties.ToggleableState
         )
         toggleState?.let {
-            info.isCheckable = true
-            when (it) {
-                ToggleableState.On -> {
-                    info.isChecked = true
-                    // Unfortunately, talback has a bug of using "checked", so we set state
-                    // description here
-                    if (role == Role.Switch && info.stateDescription == null) {
-                        info.stateDescription = view.context.resources.getString(R.string.on)
-                    }
-                }
-
-                ToggleableState.Off -> {
-                    info.isChecked = false
-                    // Unfortunately, talkback has a bug of using "not checked", so we set state
-                    // description here
-                    if (role == Role.Switch && info.stateDescription == null) {
-                        info.stateDescription = view.context.resources.getString(R.string.off)
-                    }
-                }
-
-                ToggleableState.Indeterminate -> {
-                    if (info.stateDescription == null) {
-                        info.stateDescription =
-                            view.context.resources.getString(R.string.indeterminate)
-                    }
-                }
+            if (toggleState == ToggleableState.On) {
+                info.isChecked = true
+            } else if (toggleState == ToggleableState.Off) {
+                info.isChecked = false
             }
         }
         semanticsNode.unmergedConfig.getOrNull(SemanticsProperties.Selected)?.let {
@@ -838,18 +819,7 @@
                 // Tab in native android uses selected property
                 info.isSelected = it
             } else {
-                info.isCheckable = true
                 info.isChecked = it
-                if (info.stateDescription == null) {
-                    // If a radio entry (radio button + text) is selectable, it won't have the role
-                    // RadioButton, so if we use info.isCheckable/info.isChecked, talkback will say
-                    // "checked/not checked" instead "selected/note selected".
-                    info.stateDescription = if (it) {
-                        view.context.resources.getString(R.string.selected)
-                    } else {
-                        view.context.resources.getString(R.string.not_selected)
-                    }
-                }
             }
         }
 
@@ -1065,29 +1035,6 @@
                     rangeInfo.range.endInclusive,
                     rangeInfo.current
                 )
-                // let's set state description here and use state description change events.
-                // otherwise, we need to send out type_view_selected event, as the old android
-                // versions do. But the support for type_view_selected event for progress bars
-                // maybe deprecated in talkback in the future.
-                if (info.stateDescription == null) {
-                    val valueRange = rangeInfo.range
-                    val progress = (
-                        if (valueRange.endInclusive - valueRange.start == 0f) 0f
-                        else (rangeInfo.current - valueRange.start) /
-                            (valueRange.endInclusive - valueRange.start)
-                        ).coerceIn(0f, 1f)
-
-                    // We only display 0% or 100% when it is exactly 0% or 100%.
-                    val percent = when (progress) {
-                        0f -> 0
-                        1f -> 100
-                        else -> (progress * 100).roundToInt().coerceIn(1, 99)
-                    }
-                    info.stateDescription =
-                        view.context.resources.getString(R.string.template_percent, percent)
-                }
-            } else if (info.stateDescription == null) {
-                info.stateDescription = view.context.resources.getString(R.string.in_progress)
             }
             if (semanticsNode.unmergedConfig.contains(SemanticsActions.SetProgress) &&
                 semanticsNode.enabled()
@@ -1263,12 +1210,7 @@
             }
         }
 
-        val isSpeakingNode = info.contentDescription != null || info.text != null ||
-            info.hintText != null || info.stateDescription != null || info.isCheckable
-
-        info.isScreenReaderFocusable =
-            semanticsNode.unmergedConfig.isMergingSemanticsOfDescendants ||
-                isUnmergedLeafNode && isSpeakingNode
+        info.isScreenReaderFocusable = isScreenReaderFocusable(semanticsNode)
 
         if (idToBeforeMap[virtualViewId] != null) {
             idToBeforeMap[virtualViewId]?.let { info.setTraversalBefore(view, it) }
@@ -1293,10 +1235,131 @@
         }
     }
 
-    private fun setText(
+    private fun getInfoStateDescriptionOrNull(
+        node: SemanticsNode
+    ): String? {
+        var stateDescription = node.unmergedConfig.getOrNull(SemanticsProperties.StateDescription)
+        val toggleState = node.unmergedConfig.getOrNull(SemanticsProperties.ToggleableState)
+        val role = node.unmergedConfig.getOrNull(SemanticsProperties.Role)
+
+        // Check toggle state and retrieve description accordingly
+        toggleState?.let {
+            when (it) {
+                ToggleableState.On -> {
+                    // Unfortunately, talkback has a bug of using "checked", so we set state
+                    // description here
+                    if (role == Role.Switch && stateDescription == null) {
+                        stateDescription = view.context.resources.getString(R.string.on)
+                    }
+                }
+
+                ToggleableState.Off -> {
+                    // Unfortunately, talkback has a bug of using "not checked", so we set state
+                    // description here
+                    if (role == Role.Switch && stateDescription == null) {
+                        stateDescription = view.context.resources.getString(R.string.off)
+                    }
+                }
+
+                ToggleableState.Indeterminate -> {
+                    if (stateDescription == null) {
+                        stateDescription =
+                            view.context.resources.getString(R.string.indeterminate)
+                    }
+                }
+            }
+        }
+
+        // Check Selected property and retrieve description accordingly
+        node.unmergedConfig.getOrNull(SemanticsProperties.Selected)?.let {
+            if (role != Role.Tab) {
+                if (stateDescription == null) {
+                    // If a radio entry (radio button + text) is selectable, it won't have the role
+                    // RadioButton, so if we use info.isCheckable/info.isChecked, talkback will say
+                    // "checked/not checked" instead "selected/note selected".
+                    stateDescription = if (it) {
+                        view.context.resources.getString(R.string.selected)
+                    } else {
+                        view.context.resources.getString(R.string.not_selected)
+                    }
+                }
+            }
+        }
+
+        // Check if a node has progress bar range info and retrieve description accordingly
+        val rangeInfo =
+            node.unmergedConfig.getOrNull(SemanticsProperties.ProgressBarRangeInfo)
+        rangeInfo?.let {
+            // let's set state description here and use state description change events.
+            // otherwise, we need to send out type_view_selected event, as the old android
+            // versions do. But the support for type_view_selected event for progress bars
+            // maybe deprecated in talkback in the future.
+            if (rangeInfo !== ProgressBarRangeInfo.Indeterminate) {
+                if (stateDescription == null) {
+                    val valueRange = rangeInfo.range
+                    val progress = (
+                        if (valueRange.endInclusive - valueRange.start == 0f) 0f
+                        else (rangeInfo.current - valueRange.start) /
+                            (valueRange.endInclusive - valueRange.start)
+                        ).coerceIn(0f, 1f)
+
+                    // We only display 0% or 100% when it is exactly 0% or 100%.
+                    val percent = when (progress) {
+                        0f -> 0
+                        1f -> 100
+                        else -> (progress * 100).roundToInt().coerceIn(1, 99)
+                    }
+                    stateDescription =
+                        view.context.resources.getString(R.string.template_percent, percent)
+                }
+            } else if (stateDescription == null) {
+                stateDescription = view.context.resources.getString(R.string.in_progress)
+            }
+        }
+
+        return stateDescription
+    }
+
+    private fun setStateDescription(
         node: SemanticsNode,
         info: AccessibilityNodeInfoCompat,
     ) {
+        info.stateDescription = getInfoStateDescriptionOrNull(node)
+    }
+
+    private fun getInfoIsCheckable(
+        node: SemanticsNode
+    ): Boolean {
+        var isCheckable = false
+        val toggleState = node.unmergedConfig.getOrNull(SemanticsProperties.ToggleableState)
+        val role = node.unmergedConfig.getOrNull(SemanticsProperties.Role)
+
+        toggleState?.let {
+            isCheckable = true
+        }
+
+        node.unmergedConfig.getOrNull(SemanticsProperties.Selected)?.let {
+            if (role != Role.Tab) {
+                isCheckable = true
+            }
+        }
+
+        return isCheckable
+    }
+
+    private fun setIsCheckable(
+        node: SemanticsNode,
+        info: AccessibilityNodeInfoCompat
+    ) {
+        info.isCheckable = getInfoIsCheckable(node)
+    }
+
+    // This needs to be here instead of around line 3000 because we need access to the `view`
+    // that is inside the `AndroidComposeViewAccessibilityDelegateCompat` class
+    @OptIn(InternalTextApi::class)
+    private fun getInfoText(
+        node: SemanticsNode
+    ): SpannableString? {
         val fontFamilyResolver: FontFamily.Resolver = view.fontFamilyResolver
         val editableTextToAssign = trimToSize(
             node.unmergedConfig.getTextForTextField()
@@ -1317,8 +1380,14 @@
                 ),
             ParcelSafeTextLength
         )
+        return editableTextToAssign ?: textToAssign
+    }
 
-        info.text = editableTextToAssign ?: textToAssign
+    private fun setText(
+        node: SemanticsNode,
+        info: AccessibilityNodeInfoCompat,
+    ) {
+        info.text = getInfoText(node)
     }
 
     /**
@@ -2748,10 +2817,10 @@
         }
         if (bufferedContentCaptureDisappearedNodes.isNotEmpty()) {
             session.notifyViewsDisappeared(
-                    bufferedContentCaptureDisappearedNodes
-                        .toList()
-                        .fastMap { it.toLong() }
-                        .toLongArray())
+                bufferedContentCaptureDisappearedNodes
+                    .toList()
+                    .fastMap { it.toLong() }
+                    .toLongArray())
             bufferedContentCaptureDisappearedNodes.clear()
         }
     }
@@ -3208,6 +3277,9 @@
         return false
     }
 
+private val SemanticsNode.infoContentDescriptionOrNull get() = this.unmergedConfig.getOrNull(
+    SemanticsProperties.ContentDescription)?.firstOrNull()
+
 @OptIn(ExperimentalComposeUiApi::class)
 private fun SemanticsNode.excludeLineAndPageGranularities(): Boolean {
     // text field that is not in focus
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 39c3291..e3002c8 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
@@ -1342,6 +1342,8 @@
         } else {
             resetModifierState()
         }
+        // resetModifierState detaches all nodes, so we need to re-attach them upon reuse.
+        nodes.attach()
     }
 
     override fun onDeactivate() {
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 ab6678e..c56a5d2 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
@@ -78,6 +78,13 @@
 
     internal val unmergedConfig = outerSemanticsNode.collapsedSemanticsConfiguration()
 
+    internal var isUnmergedLeafNode =
+        !isFake && replacedChildren.isEmpty() && layoutNode.findClosestParentNode {
+            it.outerSemantics
+                ?.collapsedSemanticsConfiguration()
+                ?.isMergingSemanticsOfDescendants == true
+        } == null
+
     /**
      * The [LayoutInfo] that this is associated with.
      */
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
index 24ae735..4e015c9 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
@@ -17,18 +17,22 @@
 package androidx.constraintlayout.compose.integration.macrobenchmark.target
 
 import android.os.Bundle
+import android.view.Choreographer
+import android.view.View
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage.NewMotionMessagePreview
-import androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage.NewMotionMessagePreviewWithDsl
-import androidx.compose.integration.macrobenchmark.target.motionlayout.toolbar.MotionCollapseToolbarPreview
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage.NewMotionMessagePreview
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage.NewMotionMessagePreviewWithDsl
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.toolbar.MotionCollapseToolbarPreview
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
+import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.graphs.DynamicGraphsPreview
 
 class MotionLayoutActivity : ComponentActivity() {
 
@@ -57,6 +61,9 @@
                         "CollapsibleToolbar" -> {
                             MotionCollapseToolbarPreview()
                         }
+                        "DynamicGraphs" -> {
+                            DynamicGraphsPreview()
+                        }
                         else -> {
                             throw IllegalArgumentException("No Composable with name: $name")
                         }
@@ -64,5 +71,22 @@
                 }
             }
         }
+        launchIdlenessTracking()
+    }
+
+    // Copied from LazyColumnActivity.kt
+    private fun ComponentActivity.launchIdlenessTracking() {
+        val contentView: View = findViewById(android.R.id.content)
+        val callback: Choreographer.FrameCallback = object : Choreographer.FrameCallback {
+            override fun doFrame(frameTimeNanos: Long) {
+                if (Recomposer.runningRecomposers.value.any { it.hasPendingWork }) {
+                    contentView.contentDescription = "COMPOSE-BUSY"
+                } else {
+                    contentView.contentDescription = "COMPOSE-IDLE"
+                }
+                Choreographer.getInstance().postFrameCallback(this)
+            }
+        }
+        Choreographer.getInstance().postFrameCallback(callback)
     }
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/graphs/DynamicGraphs.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/graphs/DynamicGraphs.kt
new file mode 100644
index 0000000..1ba89a0
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/graphs/DynamicGraphs.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalMotionApi::class)
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.graphs
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.ExperimentalMotionApi
+import androidx.constraintlayout.compose.MotionLayout
+import androidx.constraintlayout.compose.MotionScene
+import kotlin.random.Random
+
+/**
+ * Shows how to use MotionLayout to have animated graphs in a LazyColumn, where each graph is
+ * animated as it's revealed.
+ *
+ * Demonstrates how to dynamically create constraints based on input. See [DynamicGraph]. Where
+ * constraints are created to lay out the given values into a single graph layout.
+ */
+@Preview(group = "scroll", device = "spec:shape=Normal,width=480,height=800,unit=dp,dpi=440")
+@Composable
+fun DynamicGraphsPreview(modifier: Modifier = Modifier) {
+    val graphs = remember {
+        val list = mutableListOf<List<Float>>()
+        repeat(300) {
+            val values = FloatArray(10) { Random.nextInt(100).toFloat() + 10f }.asList()
+            list.add(values)
+        }
+        return@remember list
+    }
+    LazyColumn(modifier.testTag("LazyColumn")) {
+        items(graphs.size) {
+            Box(
+                modifier = Modifier
+                    .padding(3.dp)
+                    .height(200.dp)
+            ) {
+                DynamicGraph(graphs[it])
+            }
+        }
+    }
+}
+
+@Preview(group = "scroll", device = "spec:shape=Normal,width=480,height=800,unit=dp,dpi=440")
+@Composable
+private fun DynamicGraph(
+    values: List<Float> = listOf<Float>(12f, 32f, 21f, 32f, 2f),
+    max: Int = 100
+) {
+    val scene = remember {
+        val scale = values.map { (it * 0.8f) / max }
+        val count = values.size
+        val widthPercent = 1 / (count * 2f)
+
+        MotionScene {
+            val cols = Array(count) { i -> createRefFor("foo$i") }
+            val start1 = constraintSet {
+                createHorizontalChain(elements = cols)
+                cols.forEach {
+                    constrain(it) {
+                        width = Dimension.percent(widthPercent)
+                        height = Dimension.value(1.dp)
+                        bottom.linkTo(parent.bottom, 16.dp)
+                    }
+                }
+            }
+
+            val end1 = constraintSet {
+                createHorizontalChain(elements = cols)
+                cols.forEachIndexed { i, col ->
+                    constrain(col) {
+                        width = Dimension.percent(widthPercent)
+                        height = Dimension.percent(scale[i])
+                        bottom.linkTo(parent.bottom, 16.dp)
+                    }
+                }
+            }
+            defaultTransition(start1, end1)
+        }
+    }
+    var animateToEnd by remember { mutableStateOf(true) }
+    val progress = remember { Animatable(0f) }
+
+    // Animate on reveal
+    LaunchedEffect(animateToEnd) {
+        progress.animateTo(
+            if (animateToEnd) 1f else 0f,
+            animationSpec = tween(800)
+        )
+    }
+
+    MotionLayout(
+        modifier = Modifier
+            .background(Color(0xFF221010))
+            .fillMaxSize()
+            .clickable { animateToEnd = !animateToEnd }
+            .padding(1.dp),
+        motionScene = scene,
+        progress = progress.value
+    ) {
+        for (i in 0..values.size) {
+            Box(
+                modifier = Modifier
+                    .layoutId("foo$i")
+                    .clip(RoundedCornerShape(20.dp))
+                    .background(Color.hsv(i * 240f / values.size, 0.6f, 0.6f))
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
index 8d144a9..75c0dc8 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalMotionApi::class)
 
-package androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage
 
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Arrangement
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt
index 677182e..0fb0062 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt
index 50ab5bc..3fe6074 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.integration.macrobenchmark.target.motionlayout.toolbar
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.toolbar
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
index d15fad9..73f439c 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
@@ -24,6 +24,8 @@
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
 import kotlin.math.roundToInt
 import org.junit.Rule
 import org.junit.Test
@@ -55,10 +57,49 @@
     fun messageJson() = benchmarkRule.testNewMessage(NewMessageMode.Json)
 
     @Test
-    fun collapsibleToolbar() = benchmarkRule.testCollapsibleToolbar()
+    fun collapsibleToolbar() = benchmarkRule.motionBenchmark("CollapsibleToolbar") {
+        val column = device.findObject(By.res("LazyColumn"))
+        val bounds = column.visibleBounds
+
+        // Margin to reduce the amount of pixels scrolled and avoid the navigation pill
+        val vMargin = (bounds.height() * 0.2f).roundToInt()
+        val x = (bounds.width() * 0.5f).roundToInt()
+        val y1 = bounds.bottom - vMargin
+        val y2 = bounds.top + vMargin
+
+        // Scroll down
+        device.swipe(x, y1, x, y2, 50)
+        device.waitForIdle()
+
+        // Scroll up
+        device.swipe(x, y2, x, y1, 50)
+        device.waitForIdle()
+    }
 
     /**
-     * The base method to benchmark FrameTimings of a Composable from the macrobenchmark-app module.
+     * LazyList based layout, where every item is a MotionLayout Composable and are animated as they
+     * are revealed.
+     */
+    @Test
+    fun dynamicGraphs() = benchmarkRule.motionBenchmark("DynamicGraphs") {
+        val column = device.findObject(By.res("LazyColumn"))
+        val bounds = column.visibleBounds
+
+        // Margin to avoid swiping the navigation pill
+        val vMargin = (bounds.height() * 0.1f).roundToInt()
+        val x = (bounds.width() * 0.5f).roundToInt()
+        val y1 = bounds.bottom - vMargin
+        val y2 = bounds.top + vMargin
+
+        repeat(5) {
+            // Fast swipe upwards, to scroll down through multiple animated items at a time
+            device.swipe(x, y1, x, y2, 6)
+            device.waitForComposeIdle()
+        }
+    }
+
+    /**
+     * The base method to benchmark FrameTimings of a Composable from the macrobenchmark-target module.
      *
      * [composableName] should be a registered Composable in **MotionLayoutBenchmarkActivity**
      *
@@ -87,32 +128,13 @@
                 intent.putExtra("ComposableName", composableName)
                 startActivityAndWait(intent)
                 device.waitForIdle()
+                device.waitForComposeIdle()
                 setupBlock()
             },
             measureBlock = measureBlock
         )
     }
 
-    private fun MacrobenchmarkRule.testCollapsibleToolbar() =
-        motionBenchmark("CollapsibleToolbar") {
-            val column = device.findObject(By.res("LazyColumn"))
-            val bounds = column.visibleBounds
-
-            // Margin to reduce the amount of pixels scrolled
-            val vMargin = (bounds.height() * 0.2f).roundToInt()
-            val x = (bounds.width() * 0.5f).roundToInt()
-            val y1 = bounds.bottom - vMargin
-            val y2 = bounds.top + vMargin
-
-            // Swipe down
-            device.swipe(x, y1, x, y2, 50)
-            device.waitForIdle()
-
-            // Swipe up
-            device.swipe(x, y2, x, y1, 50)
-            device.waitForIdle()
-        }
-
     private fun MacrobenchmarkRule.testNewMessage(
         mode: NewMessageMode
     ) {
@@ -138,4 +160,8 @@
         Json("NewMessageJson"),
         Dsl("NewMessageDsl")
     }
+
+    private fun UiDevice.waitForComposeIdle(timeoutMs: Long = 3000) {
+        wait(Until.findObject(By.desc("COMPOSE-IDLE")), timeoutMs)
+    }
 }
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
index ef402aa..0098c1f 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
@@ -67,10 +67,12 @@
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.roundToIntRect
 import androidx.compose.ui.unit.size
 import androidx.compose.ui.unit.sp
+import androidx.compose.ui.unit.width
 import androidx.constraintlayout.compose.test.R
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -599,6 +601,64 @@
         }
     }
 
+    @Test
+    fun testRemeasureOnContentChanged() {
+        val progress = mutableStateOf(0f)
+        val textContent = mutableStateOf("Foo")
+
+        rule.setContent {
+            WithConsistentTextStyle {
+                MotionLayout(
+                    modifier = Modifier
+                        .size(300.dp)
+                        .background(Color.LightGray),
+                    motionScene = MotionScene {
+                        // Text at wrap_content, animated from top of the layout to the bottom
+                        val textRef = createRefFor("text")
+                        defaultTransition(
+                            from = constraintSet {
+                                constrain(textRef) {
+                                    centerHorizontallyTo(parent)
+                                    centerVerticallyTo(parent, 0f)
+                                }
+                            },
+                            to = constraintSet {
+                                constrain(textRef) {
+                                    centerHorizontallyTo(parent)
+                                    centerVerticallyTo(parent, 1f)
+                                }
+                            }
+                        )
+                    },
+                    progress = progress.value
+                ) {
+                    Text(
+                        text = textContent.value,
+                        fontSize = 10.sp,
+                        modifier = Modifier.layoutTestId("text")
+                    )
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        var actualTextSize = rule.onNodeWithTag("text").getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+
+        progress.value = 0.5f
+        rule.waitForIdle()
+        actualTextSize = rule.onNodeWithTag("text").getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+
+        textContent.value = "FooBar"
+        rule.waitForIdle()
+        actualTextSize = rule.onNodeWithTag("text").getUnclippedBoundsInRoot()
+        assertEquals(36, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+    }
+
     private fun Color.toHexString(): String = toArgb().toUInt().toString(16)
 }
 
@@ -606,15 +666,7 @@
 @Composable
 private fun CustomTextSize(modifier: Modifier, progress: Float) {
     val context = LocalContext.current
-    @Suppress("DEPRECATION")
-    CompositionLocalProvider(
-        LocalDensity provides Density(1f, 1f),
-        LocalTextStyle provides TextStyle(
-            fontFamily = FontFamily.Monospace,
-            fontWeight = FontWeight.Normal,
-            platformStyle = PlatformTextStyle(includeFontPadding = true)
-        )
-    ) {
+    WithConsistentTextStyle {
         MotionLayout(
             motionScene = MotionScene(
                 content = context
@@ -652,4 +704,27 @@
             )
         }
     }
+}
+
+/**
+ * Provides composition locals that help making Text produce consistent measurements across multiple
+ * devices.
+ *
+ * Be aware that this makes it so that 1.dp = 1px. So the layout will look significantly different
+ * than expected.
+ */
+@Composable
+private fun WithConsistentTextStyle(
+    content: @Composable () -> Unit
+) {
+    @Suppress("DEPRECATION")
+    CompositionLocalProvider(
+        LocalDensity provides Density(1f, 1f),
+        LocalTextStyle provides TextStyle(
+            fontFamily = FontFamily.Monospace,
+            fontWeight = FontWeight.Normal,
+            platformStyle = PlatformTextStyle(includeFontPadding = true)
+        ),
+        content = content
+    )
 }
\ No newline at end of file
diff --git a/datastore/datastore-core-okio/api/current.txt b/datastore/datastore-core-okio/api/current.txt
index bbcb949..08053b5 100644
--- a/datastore/datastore-core-okio/api/current.txt
+++ b/datastore/datastore-core-okio/api/current.txt
@@ -9,7 +9,7 @@
   }
 
   public final class OkioStorage<T> implements androidx.datastore.core.Storage<T> {
-    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, kotlin.jvm.functions.Function0<okio.Path> producePath);
+    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, optional kotlin.jvm.functions.Function2<? super okio.Path,? super okio.FileSystem,? extends androidx.datastore.core.InterProcessCoordinator> coordinatorProducer, kotlin.jvm.functions.Function0<okio.Path> producePath);
     method public androidx.datastore.core.StorageConnection<T> createConnection();
   }
 
diff --git a/datastore/datastore-core-okio/api/public_plus_experimental_current.txt b/datastore/datastore-core-okio/api/public_plus_experimental_current.txt
index bbcb949..08053b5 100644
--- a/datastore/datastore-core-okio/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-core-okio/api/public_plus_experimental_current.txt
@@ -9,7 +9,7 @@
   }
 
   public final class OkioStorage<T> implements androidx.datastore.core.Storage<T> {
-    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, kotlin.jvm.functions.Function0<okio.Path> producePath);
+    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, optional kotlin.jvm.functions.Function2<? super okio.Path,? super okio.FileSystem,? extends androidx.datastore.core.InterProcessCoordinator> coordinatorProducer, kotlin.jvm.functions.Function0<okio.Path> producePath);
     method public androidx.datastore.core.StorageConnection<T> createConnection();
   }
 
diff --git a/datastore/datastore-core-okio/api/res-current.txt b/datastore/datastore-core-okio/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/datastore/datastore-core-okio/api/res-current.txt
diff --git a/datastore/datastore-core-okio/api/restricted_current.txt b/datastore/datastore-core-okio/api/restricted_current.txt
index bbcb949..08053b5 100644
--- a/datastore/datastore-core-okio/api/restricted_current.txt
+++ b/datastore/datastore-core-okio/api/restricted_current.txt
@@ -9,7 +9,7 @@
   }
 
   public final class OkioStorage<T> implements androidx.datastore.core.Storage<T> {
-    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, kotlin.jvm.functions.Function0<okio.Path> producePath);
+    ctor public OkioStorage(okio.FileSystem fileSystem, androidx.datastore.core.okio.OkioSerializer<T> serializer, optional kotlin.jvm.functions.Function2<? super okio.Path,? super okio.FileSystem,? extends androidx.datastore.core.InterProcessCoordinator> coordinatorProducer, kotlin.jvm.functions.Function0<okio.Path> producePath);
     method public androidx.datastore.core.StorageConnection<T> createConnection();
   }
 
diff --git a/datastore/datastore-core-okio/build.gradle b/datastore/datastore-core-okio/build.gradle
index 9d4aa01..d1c5ba1 100644
--- a/datastore/datastore-core-okio/build.gradle
+++ b/datastore/datastore-core-okio/build.gradle
@@ -27,9 +27,7 @@
 def enableNative = KmpPlatformsKt.enableNative(project)
 
 androidXMultiplatform {
-    jvm() {
-        withJava()
-    }
+    jvm()
     mac()
     linux()
     ios()
diff --git a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioStorage.kt b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioStorage.kt
index e896d97..e2ff9fb 100644
--- a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioStorage.kt
+++ b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioStorage.kt
@@ -16,6 +16,7 @@
 
 package androidx.datastore.core.okio
 
+import androidx.datastore.core.InterProcessCoordinator
 import androidx.datastore.core.ReadScope
 import androidx.datastore.core.Storage
 import androidx.datastore.core.StorageConnection
@@ -36,10 +37,20 @@
 
 /**
  * OKIO implementation of the Storage interface, providing cross platform IO using the OKIO library.
+ *
+ * @param fileSystem The file system to perform IO operations on.
+ * @param serializer The serializer for `T`.
+ * @param coordinatorProducer The producer to provide [InterProcessCoordinator] that coordinates IO
+ * operations across processes if needed. By default it provides single process coordinator, which
+ * doesn't support cross process use cases.
+ * @param producePath The file producer that returns the file path that will be read and written.
  */
 public class OkioStorage<T>(
     private val fileSystem: FileSystem,
     private val serializer: OkioSerializer<T>,
+    private val coordinatorProducer: (Path, FileSystem) -> InterProcessCoordinator = { _, _ ->
+        createSingleProcessCoordinator()
+    },
     private val producePath: () -> Path
 ) : Storage<T> {
     private val canonicalPath by lazy {
@@ -63,7 +74,12 @@
                 activeFiles.add(path)
             }
         }
-        return OkioStorageConnection(fileSystem, canonicalPath, serializer) {
+        return OkioStorageConnection(
+            fileSystem,
+            canonicalPath,
+            serializer,
+            coordinatorProducer(canonicalPath, fileSystem)
+        ) {
             synchronized(activeFilesLock) {
                 activeFiles.remove(canonicalPath.toString())
             }
@@ -74,6 +90,7 @@
         internal val activeFiles = mutableSetOf<String>()
 
         class Sync : SynchronizedObject()
+
         internal val activeFilesLock = Sync()
     }
 }
@@ -82,13 +99,14 @@
     private val fileSystem: FileSystem,
     private val path: Path,
     private val serializer: OkioSerializer<T>,
+    override val coordinator: InterProcessCoordinator,
     private val onClose: () -> Unit
 ) : StorageConnection<T> {
 
     private val closed = AtomicBoolean(false)
+
     // TODO:(b/233402915) support multiple readers
     private val transactionMutex = Mutex()
-    override val coordinator = createSingleProcessCoordinator()
 
     override suspend fun <R> readScope(
         block: suspend ReadScope<T>.(locked: Boolean) -> R
@@ -178,6 +196,7 @@
     override fun close() {
         closed = true
     }
+
     protected fun checkClose() {
         check(!closed) { "This scope has already been closed." }
     }
diff --git a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
index 3df190d..f2b88b1 100644
--- a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
+++ b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
@@ -28,15 +28,15 @@
 import androidx.datastore.TestingSerializerConfig
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlin.test.Test
 import kotlin.test.BeforeTest
 import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.async
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.yield
 import okio.FileSystem
 import okio.IOException
@@ -316,7 +316,7 @@
             }
             FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "new-temp-file.tmp"
         }
-        val storage = OkioStorage(fileSystem, testingSerializer, fileProducer)
+        val storage = OkioStorage(fileSystem, testingSerializer, producePath = fileProducer)
 
         assertThrows<IOException> { storage.createConnection().use {
             it.readData()
diff --git a/datastore/datastore-core/src/androidAndroidTest/AndroidManifest.xml b/datastore/datastore-core/src/androidAndroidTest/AndroidManifest.xml
index 1fd5182..791dfd1 100644
--- a/datastore/datastore-core/src/androidAndroidTest/AndroidManifest.xml
+++ b/datastore/datastore-core/src/androidAndroidTest/AndroidManifest.xml
@@ -18,45 +18,85 @@
 
     <application>
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$SimpleUpdateService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$SimpleUpdateFileService"
             android:enabled="true"
             android:exported="false"
-            android:process=":SimpleUpdateService" />
+            android:process=":SimpleUpdateFileService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateWriterService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$SimpleUpdateOkioService"
             android:enabled="true"
             android:exported="false"
-            android:process=":ConcurrentReadUpdateWriterService" />
+            android:process=":SimpleUpdateOkioService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateReaderService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateWriterFileService"
             android:enabled="true"
             android:exported="false"
-            android:process=":ConcurrentReadUpdateReaderService" />
+            android:process=":ConcurrentReadUpdateWriterFileService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateWriterOkioService"
             android:enabled="true"
             android:exported="false"
-            android:process=":InterleavedUpdateDataService" />
+            android:process=":ConcurrentReadUpdateWriterOkioService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataWithReadService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateReaderFileService"
             android:enabled="true"
             android:exported="false"
-            android:process=":InterleavedUpdateDataWithReadService" />
+            android:process=":ConcurrentReadUpdateReaderFileService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$FailedUpdateDataService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateReaderOkioService"
             android:enabled="true"
             android:exported="false"
-            android:process=":FailedUpdateDataService" />
+            android:process=":ConcurrentReadUpdateReaderOkioService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$CancelledUpdateDataService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataFileService"
             android:enabled="true"
             android:exported="false"
-            android:process=":CancelledUpdateDataService" />
+            android:process=":InterleavedUpdateDataFileService" />
         <service
-            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedHandlerUpdateDataService"
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataOkioService"
             android:enabled="true"
             android:exported="false"
-            android:process=":InterleavedHandlerUpdateDataService" />
+            android:process=":InterleavedUpdateDataOkioService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataWithReadFileService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":InterleavedUpdateDataWithReadFileService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataWithReadOkioService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":InterleavedUpdateDataWithReadOkioService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$FailedUpdateDataFileService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":FailedUpdateDataFileService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$FailedUpdateDataOkioService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":FailedUpdateDataOkioService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$CancelledUpdateDataFileService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":CancelledUpdateDataFileService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$CancelledUpdateDataOkioService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":CancelledUpdateDataOkioService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedHandlerUpdateDataFileService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":InterleavedHandlerUpdateDataFileService" />
+        <service
+            android:name="androidx.datastore.core.MultiProcessDataStoreMultiProcessTest$InterleavedHandlerUpdateDataOkioService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":InterleavedHandlerUpdateDataOkioService" />
     </application>
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
index 88d20b0..1dbe386 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
@@ -74,7 +74,7 @@
     private lateinit var testFailure: Throwable
 
     // It should be setup in `beforeTest`
-    internal lateinit var store: DataStoreImpl<FooProto>
+    internal lateinit var store: DataStore<FooProto>
 
     override fun onBind(intent: Intent): IBinder {
         return messenger.getBinder()
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
index 13bd3d7..27d5641 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
@@ -20,6 +20,8 @@
 import android.os.Bundle
 import androidx.datastore.core.handlers.NoOpCorruptionHandler
 import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.core.okio.OkioSerializer
+import androidx.datastore.core.okio.OkioStorage
 import androidx.test.core.app.ApplicationProvider
 import androidx.testing.TestMessageProto.FooProto
 import com.google.common.truth.Truth.assertThat
@@ -42,6 +44,8 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
+import okio.FileSystem
+import okio.Path.Companion.toPath
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -50,10 +54,17 @@
 import org.junit.runners.JUnit4
 
 private const val PATH_BUNDLE_KEY: String = "path"
+private const val STORAGE_BUNDLE_KEY: String = "storage"
+private const val STORAGE_FILE: String = "FILE"
+private const val STORAGE_OKIO: String = "OKIO"
 private val PROTO_SERIALIZER: Serializer<FooProto> = ProtoSerializer<FooProto>(
     FooProto.getDefaultInstance(),
     ExtensionRegistryLite.getEmptyRegistry()
 )
+private val PROTO_OKIO_SERIALIZER: OkioSerializer<FooProto> = ProtoOkioSerializer<FooProto>(
+    FooProto.getDefaultInstance(),
+    ExtensionRegistryLite.getEmptyRegistry()
+)
 private const val TEST_TEXT: String = "abc"
 internal val WRITE_TEXT: (FooProto) -> FooProto = { f: FooProto ->
     f.toBuilder().setText(TEST_TEXT).build()
@@ -62,7 +73,7 @@
     f.toBuilder().setBoolean(true).build()
 }
 private val INCREMENT_INTEGER: (FooProto) -> FooProto = { f: FooProto ->
-    f.toBuilder().setInteger(f.getInteger() + 1).build()
+    f.toBuilder().setInteger(f.integer + 1).build()
 }
 
 private val DEFAULT_FOO: FooProto = FooProto.getDefaultInstance()
@@ -71,25 +82,43 @@
 private val FOO_WITH_TEXT_AND_BOOLEAN: FooProto =
     FooProto.newBuilder().setText(TEST_TEXT).setBoolean(true).build()
 
+private val FILESYSTEM = FileSystem.SYSTEM
+
 @ExperimentalCoroutinesApi
 private fun createDataStore(
     bundle: Bundle,
     scope: TestScope,
-    corruptionHandler: CorruptionHandler<FooProto> = NoOpCorruptionHandler<FooProto>()
-): DataStoreImpl<FooProto> {
+    corruptionHandler: CorruptionHandler<FooProto> = NoOpCorruptionHandler<FooProto>(),
+    context: CoroutineContext = UnconfinedTestDispatcher()
+): DataStore<FooProto> {
     val file = File(bundle.getString(PATH_BUNDLE_KEY)!!)
     val produceFile = { file }
-    return DataStoreImpl<FooProto>(
-        storage = FileStorage(
+    val variant = StorageVariant.valueOf(bundle.getString(STORAGE_BUNDLE_KEY)!!)
+    val storage = if (variant == StorageVariant.FILE) {
+        FileStorage(
             PROTO_SERIALIZER,
-            { MultiProcessCoordinator(UnconfinedTestDispatcher(), it) },
+            { MultiProcessCoordinator(context, it) },
             produceFile
-        ),
+        )
+    } else {
+        OkioStorage(
+            FILESYSTEM,
+            PROTO_OKIO_SERIALIZER,
+            { _, _ -> MultiProcessCoordinator(context, file) },
+            { file.absolutePath.toPath() }
+        )
+    }
+    return DataStoreImpl(
+        storage = storage,
         scope = scope,
         corruptionHandler = corruptionHandler
     )
 }
 
+internal enum class StorageVariant(val storage: String) {
+    FILE(STORAGE_FILE), OKIO(STORAGE_OKIO)
+}
+
 @OptIn(DelicateCoroutinesApi::class)
 @ExperimentalCoroutinesApi
 @RunWith(JUnit4::class)
@@ -101,34 +130,15 @@
     private lateinit var dataStoreContext: CoroutineContext
     private lateinit var dataStoreScope: TestScope
 
-    private val protoSerializer: Serializer<FooProto> = ProtoSerializer<FooProto>(
-        FooProto.getDefaultInstance(),
-        ExtensionRegistryLite.getEmptyRegistry()
-    )
     private val mainContext: Context = ApplicationProvider.getApplicationContext()
 
-    private fun createDataStoreBundle(path: String): Bundle {
+    private fun createDataStoreBundle(path: String, variant: StorageVariant): Bundle {
         val data = Bundle()
         data.putString(PATH_BUNDLE_KEY, path)
+        data.putString(STORAGE_BUNDLE_KEY, variant.storage)
         return data
     }
 
-    private fun createDataStore(
-        bundle: Bundle,
-        scope: TestScope
-    ): DataStoreImpl<FooProto> {
-        val file = File(bundle.getString(PATH_BUNDLE_KEY)!!)
-        val produceFile = { file }
-        return DataStoreImpl<FooProto>(
-            storage = FileStorage(
-                protoSerializer,
-                { MultiProcessCoordinator(dataStoreContext, it) },
-                produceFile
-            ),
-            scope = scope
-        )
-    }
-
     @Before
     fun setUp() {
         testFile = tempFolder.newFile()
@@ -137,22 +147,32 @@
     }
 
     @Test
-    fun testSimpleUpdateData() = runTest {
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope)
-        val connection: BlockingServiceConnection =
-            setUpService(mainContext, SimpleUpdateService::class.java, testData)
+    fun testSimpleUpdateData_file() = testSimpleUpdateData_runner(StorageVariant.FILE)
 
-        assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
+    @Test
+    fun testSimpleUpdateData_okio() = testSimpleUpdateData_runner(StorageVariant.OKIO)
 
-        // Other proc commits TEST_TEXT update
-        signalService(connection)
+    private fun testSimpleUpdateData_runner(variant: StorageVariant) =
+        runTest(dispatchTimeoutMs = 10000) {
+            val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+            val dataStore: DataStore<FooProto> =
+                createDataStore(testData, dataStoreScope, context = dataStoreContext)
+            val serviceClasses = mapOf(
+                StorageVariant.FILE to SimpleUpdateFileService::class,
+                StorageVariant.OKIO to SimpleUpdateOkioService::class
+            )
+            val connection: BlockingServiceConnection =
+                setUpService(mainContext, serviceClasses[variant]!!.java, testData)
 
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
-    }
+            assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
 
-    class SimpleUpdateService(
+            // Other proc commits TEST_TEXT update
+            signalService(connection)
+
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+        }
+
+    open class SimpleUpdateFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -166,37 +186,53 @@
         }
     }
 
+    class SimpleUpdateOkioService : SimpleUpdateFileService()
+
     @Test
-    fun testConcurrentReadUpdate() = runTest {
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope)
-        val writerConnection: BlockingServiceConnection =
-            setUpService(mainContext, ConcurrentReadUpdateWriterService::class.java, testData)
+    fun testConcurrentReadUpdate_file() = testConcurrentReadUpdate_runner(StorageVariant.FILE)
 
-        // Start with TEST_TEXT
-        dataStore.updateData { f: FooProto -> WRITE_TEXT(f) }
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+    @Test
+    fun testConcurrentReadUpdate_okio() = testConcurrentReadUpdate_runner(StorageVariant.OKIO)
 
-        // Writer process starts (but does not yet commit) "true"
-        signalService(writerConnection)
+    private fun testConcurrentReadUpdate_runner(variant: StorageVariant) =
+        runTest(dispatchTimeoutMs = 10000) {
+            val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+            val dataStore: DataStore<FooProto> =
+                createDataStore(testData, dataStoreScope, context = dataStoreContext)
+            val writerServiceClasses = mapOf(
+                StorageVariant.FILE to ConcurrentReadUpdateWriterFileService::class,
+                StorageVariant.OKIO to ConcurrentReadUpdateWriterOkioService::class
+            )
+            val writerConnection: BlockingServiceConnection =
+                setUpService(mainContext, writerServiceClasses[variant]!!.java, testData)
 
-        // We can continue reading datastore while the writer process is mid-write
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+            // Start with TEST_TEXT
+            dataStore.updateData { f: FooProto -> WRITE_TEXT(f) }
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
 
-        // New processes that start in the meantime can also read
-        val readerConnection: BlockingServiceConnection =
-            setUpService(mainContext, ConcurrentReadUpdateReaderService::class.java, testData)
-        signalService(readerConnection)
+            // Writer process starts (but does not yet commit) "true"
+            signalService(writerConnection)
 
-        // The other process finishes writing "true"; we (and other readers) should pick up the new data
-        signalService(writerConnection)
+            // We can continue reading datastore while the writer process is mid-write
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
 
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
-        signalService(readerConnection)
-    }
+            val readerServiceClasses = mapOf(
+                StorageVariant.FILE to ConcurrentReadUpdateReaderFileService::class,
+                StorageVariant.OKIO to ConcurrentReadUpdateReaderOkioService::class
+            )
+            // New processes that start in the meantime can also read
+            val readerConnection: BlockingServiceConnection =
+                setUpService(mainContext, readerServiceClasses[variant]!!.java, testData)
+            signalService(readerConnection)
 
-    class ConcurrentReadUpdateWriterService(
+            // The other process finishes writing "true"; we (and other readers) should pick up the new data
+            signalService(writerConnection)
+
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
+            signalService(readerConnection)
+        }
+
+    open class ConcurrentReadUpdateWriterFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -211,7 +247,9 @@
         }
     }
 
-    class ConcurrentReadUpdateReaderService(
+    class ConcurrentReadUpdateWriterOkioService : ConcurrentReadUpdateWriterFileService()
+
+    open class ConcurrentReadUpdateReaderFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -225,40 +263,52 @@
         }
     }
 
+    class ConcurrentReadUpdateReaderOkioService : ConcurrentReadUpdateReaderFileService()
+
     @Test
-    fun testInterleavedUpdateData() = runTest(UnconfinedTestDispatcher()) {
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope)
-        val connection: BlockingServiceConnection =
-            setUpService(mainContext, InterleavedUpdateDataService::class.java, testData)
+    fun testInterleavedUpdateData_file() = testInterleavedUpdateData_runner(StorageVariant.FILE)
 
-        // Other proc starts TEST_TEXT update, then waits for signal
-        signalService(connection)
+    @Test
+    fun testInterleavedUpdateData_okio() = testInterleavedUpdateData_runner(StorageVariant.OKIO)
 
-        // We start "true" update, then wait for condition
-        val condition = CompletableDeferred<Unit>()
-        val write = async(newSingleThreadContext("blockedWriter")) {
-            dataStore.updateData {
-                condition.await()
-                WRITE_BOOLEAN(it)
-            }
-        }
+    private fun testInterleavedUpdateData_runner(variant: StorageVariant) =
+        runTest(UnconfinedTestDispatcher(), dispatchTimeoutMs = 10000) {
+            val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+            val dataStore: DataStore<FooProto> =
+                createDataStore(testData, dataStoreScope, context = dataStoreContext)
+            val serviceClasses = mapOf(
+                StorageVariant.FILE to InterleavedUpdateDataFileService::class,
+                StorageVariant.OKIO to InterleavedUpdateDataOkioService::class
+            )
+            val connection: BlockingServiceConnection =
+                setUpService(mainContext, serviceClasses[variant]!!.java, testData)
 
-        // Allow the other proc's update to run to completion, then allow ours to run to completion
-        val unblockOurUpdate = async {
-            delay(100)
+            // Other proc starts TEST_TEXT update, then waits for signal
             signalService(connection)
-            condition.complete(Unit)
+
+            // We start "true" update, then wait for condition
+            val condition = CompletableDeferred<Unit>()
+            val write = async(newSingleThreadContext("blockedWriter")) {
+                dataStore.updateData {
+                    condition.await()
+                    WRITE_BOOLEAN(it)
+                }
+            }
+
+            // Allow the other proc's update to run to completion, then allow ours to run to completion
+            val unblockOurUpdate = async {
+                delay(100)
+                signalService(connection)
+                condition.complete(Unit)
+            }
+
+            unblockOurUpdate.await()
+            write.await()
+
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
         }
 
-        unblockOurUpdate.await()
-        write.await()
-
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
-    }
-
-    class InterleavedUpdateDataService(
+    open class InterleavedUpdateDataFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -273,55 +323,75 @@
         }
     }
 
+    class InterleavedUpdateDataOkioService : InterleavedUpdateDataFileService()
+
     @Test
-    fun testInterleavedUpdateDataWithLocalRead() = runTest(UnconfinedTestDispatcher()) {
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope)
-        val connection: BlockingServiceConnection =
-            setUpService(mainContext, InterleavedUpdateDataWithReadService::class.java, testData)
+    fun testInterleavedUpdateDataWithLocalRead_file() =
+        testInterleavedUpdateDataWithLocalRead_runner(StorageVariant.FILE)
 
-        // Invalidate any local cache
-        assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
-        signalService(connection)
+    @Test
+    fun testInterleavedUpdateDataWithLocalRead_okio() =
+        testInterleavedUpdateDataWithLocalRead_runner(StorageVariant.OKIO)
 
-        // Queue and start local write
-        val writeStarted = CompletableDeferred<Unit>()
-        val finishWrite = CompletableDeferred<Unit>()
+    private fun testInterleavedUpdateDataWithLocalRead_runner(variant: StorageVariant) =
+        runTest(UnconfinedTestDispatcher(), dispatchTimeoutMs = 10000) {
+            val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+            val dataStore: DataStore<FooProto> =
+                createDataStore(testData, dataStoreScope, context = dataStoreContext)
+            val serviceClasses = mapOf(
+                StorageVariant.FILE to InterleavedUpdateDataWithReadFileService::class,
+                StorageVariant.OKIO to InterleavedUpdateDataWithReadOkioService::class
+            )
+            val connection: BlockingServiceConnection =
+                setUpService(
+                    mainContext,
+                    serviceClasses[variant]!!.java,
+                    testData
+                )
 
-        val write = async {
-            dataStore.updateData {
-                writeStarted.complete(Unit)
-                finishWrite.await()
-                FOO_WITH_TEXT
-            }
-        }
-        writeStarted.await()
-
-        // Queue remote write
-        signalService(connection)
-
-        // Local uncached read; this should see data initially written remotely.
-        assertThat(dataStore.data.first()).isEqualTo(FooProto.newBuilder().setInteger(1).build())
-
-        // Unblock writes; the local write is delayed to ensure the remote write remains blocked.
-        val remoteWrite = async(newSingleThreadContext("blockedWriter")) {
+            // Invalidate any local cache
+            assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
             signalService(connection)
+
+            // Queue and start local write
+            val writeStarted = CompletableDeferred<Unit>()
+            val finishWrite = CompletableDeferred<Unit>()
+
+            val write = async {
+                dataStore.updateData {
+                    writeStarted.complete(Unit)
+                    finishWrite.await()
+                    FOO_WITH_TEXT
+                }
+            }
+            writeStarted.await()
+
+            // Queue remote write
+            signalService(connection)
+
+            // Local uncached read; this should see data initially written remotely.
+            assertThat(dataStore.data.first()).isEqualTo(
+                FooProto.newBuilder().setInteger(1).build()
+            )
+
+            // Unblock writes; the local write is delayed to ensure the remote write remains blocked.
+            val remoteWrite = async(newSingleThreadContext("blockedWriter")) {
+                signalService(connection)
+            }
+
+            val localWrite = async(newSingleThreadContext("unblockLocalWrite")) {
+                delay(500)
+                finishWrite.complete(Unit)
+                write.await()
+            }
+
+            localWrite.await()
+            remoteWrite.await()
+
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
         }
 
-        val localWrite = async(newSingleThreadContext("unblockLocalWrite")) {
-            delay(500)
-            finishWrite.complete(Unit)
-            write.await()
-        }
-
-        localWrite.await()
-        remoteWrite.await()
-
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
-    }
-
-    class InterleavedUpdateDataWithReadService(
+    open class InterleavedUpdateDataWithReadFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -345,13 +415,28 @@
         }
     }
 
+    class InterleavedUpdateDataWithReadOkioService : InterleavedUpdateDataWithReadFileService()
+
     @Test
-    fun testUpdateDataExceptionUnblocksOtherProcessFromWriting() = runTest {
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope)
+    fun testUpdateDataExceptionUnblocksOtherProcessFromWriting_file() =
+        testUpdateDataExceptionUnblocksOtherProcessFromWriting_runner(StorageVariant.FILE)
+
+    @Test
+    fun testUpdateDataExceptionUnblocksOtherProcessFromWriting_okio() =
+        testUpdateDataExceptionUnblocksOtherProcessFromWriting_runner(StorageVariant.OKIO)
+
+    private fun testUpdateDataExceptionUnblocksOtherProcessFromWriting_runner(
+        variant: StorageVariant
+    ) = runTest(dispatchTimeoutMs = 10000) {
+        val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+        val dataStore: DataStore<FooProto> =
+            createDataStore(testData, dataStoreScope, context = dataStoreContext)
+        val serviceClasses = mapOf(
+            StorageVariant.FILE to FailedUpdateDataFileService::class,
+            StorageVariant.OKIO to FailedUpdateDataOkioService::class
+        )
         val connection: BlockingServiceConnection =
-            setUpService(mainContext, FailedUpdateDataService::class.java, testData)
+            setUpService(mainContext, serviceClasses[variant]!!.java, testData)
 
         val blockWrite = CompletableDeferred<Unit>()
         val waitForWrite = CompletableDeferred<Unit>()
@@ -381,7 +466,7 @@
         assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
     }
 
-    class FailedUpdateDataService(
+    open class FailedUpdateDataFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -395,16 +480,29 @@
         }
     }
 
+    class FailedUpdateDataOkioService : FailedUpdateDataFileService()
+
     @Test
-    fun testUpdateDataCancellationUnblocksOtherProcessFromWriting() = runTest(
-        UnconfinedTestDispatcher()
-    ) {
+    fun testUpdateDataCancellationUnblocksOtherProcessFromWriting_file() =
+        testUpdateDataCancellationUnblocksOtherProcessFromWriting_runner(StorageVariant.FILE)
+
+    @Test
+    fun testUpdateDataCancellationUnblocksOtherProcessFromWriting_okio() =
+        testUpdateDataCancellationUnblocksOtherProcessFromWriting_runner(StorageVariant.OKIO)
+
+    private fun testUpdateDataCancellationUnblocksOtherProcessFromWriting_runner(
+        variant: StorageVariant
+    ) = runTest(UnconfinedTestDispatcher(), dispatchTimeoutMs = 10000) {
         val localScope = TestScope(UnconfinedTestDispatcher() + Job())
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, localScope)
+        val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+        val dataStore: DataStore<FooProto> =
+            createDataStore(testData, localScope, context = dataStoreContext)
+        val serviceClasses = mapOf(
+            StorageVariant.FILE to CancelledUpdateDataFileService::class,
+            StorageVariant.OKIO to CancelledUpdateDataOkioService::class
+        )
         val connection: BlockingServiceConnection =
-            setUpService(mainContext, CancelledUpdateDataService::class.java, testData)
+            setUpService(mainContext, serviceClasses[variant]!!.java, testData)
 
         val blockWrite = CompletableDeferred<Unit>()
 
@@ -433,7 +531,7 @@
     // A duplicate from CancelledUpdateDataService to make sure Android framework would create a
     // new process for this test. Otherwise the test would hang infinitely because the tests bind
     // to an existing service created by the previous test.
-    class CancelledUpdateDataService(
+    open class CancelledUpdateDataFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -447,35 +545,47 @@
         }
     }
 
+    class CancelledUpdateDataOkioService : CancelledUpdateDataFileService()
+
     @Test
-    fun testReadUpdateCorrupt() = runTest {
-        FileOutputStream(testFile).use {
-            OutputStreamWriter(it).write("garbage")
-        }
-        val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
-        val connection: BlockingServiceConnection =
-            setUpService(mainContext, InterleavedHandlerUpdateDataService::class.java, testData)
-        val corruptionHandler = ReplaceFileCorruptionHandler<FooProto> {
+    fun testReadUpdateCorrupt_file() = testReadUpdateCorrupt_runner(StorageVariant.FILE)
+
+    @Test
+    fun testReadUpdateCorrupt_okio() = testReadUpdateCorrupt_runner(StorageVariant.OKIO)
+
+    private fun testReadUpdateCorrupt_runner(variant: StorageVariant) =
+        runTest(dispatchTimeoutMs = 10000) {
+            FileOutputStream(testFile).use {
+                OutputStreamWriter(it).write("garbage")
+            }
+            val testData: Bundle = createDataStoreBundle(testFile.absolutePath, variant)
+            val serviceClasses = mapOf(
+                StorageVariant.FILE to InterleavedHandlerUpdateDataFileService::class,
+                StorageVariant.OKIO to InterleavedHandlerUpdateDataOkioService::class
+            )
+            val connection: BlockingServiceConnection =
+                setUpService(mainContext, serviceClasses[variant]!!.java, testData)
+            val corruptionHandler = ReplaceFileCorruptionHandler<FooProto> {
+                signalService(connection)
+                FOO_WITH_TEXT_AND_BOOLEAN
+            }
+            val dataStore: DataStore<FooProto> =
+                createDataStore(testData, dataStoreScope, corruptionHandler, dataStoreContext)
+
+            // Other proc starts TEST_TEXT then waits for signal within handler
             signalService(connection)
-            FOO_WITH_TEXT_AND_BOOLEAN
+
+            assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+
+            // version file should be ready at this point
+            val sharedCounter = SharedCounter.create {
+                File(testFile.absolutePath + ".version")
+            }
+            // only 1 write should be done to handle the corruption, so version is incremented by 1
+            assertThat(sharedCounter.getValue()).isEqualTo(1)
         }
-        val dataStore: DataStoreImpl<FooProto> =
-            createDataStore(testData, dataStoreScope, corruptionHandler)
 
-        // Other proc starts TEST_TEXT then waits for signal within handler
-        signalService(connection)
-
-        assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
-
-        // version file should be ready at this point
-        val sharedCounter = SharedCounter.create {
-            File(testFile.absolutePath + ".version")
-        }
-        // only 1 write should be done to handle the corruption, so version is incremented by 1
-        assertThat(sharedCounter.getValue()).isEqualTo(1)
-    }
-
-    class InterleavedHandlerUpdateDataService(
+    open class InterleavedHandlerUpdateDataFileService(
         private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
     ) : DirectTestService() {
         override fun beforeTest(testData: Bundle) {
@@ -493,4 +603,6 @@
             }
         }
     }
+
+    class InterleavedHandlerUpdateDataOkioService : InterleavedHandlerUpdateDataFileService()
 }
\ No newline at end of file
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt
new file mode 100644
index 0000000..24288ef
--- /dev/null
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.datastore.core
+
+import androidx.datastore.FileTestIO
+import androidx.datastore.JavaIOFile
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.ObsoleteCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
+@InternalCoroutinesApi
+class MultiProcessDataStoreSingleProcessFileTest :
+    MultiProcessDataStoreSingleProcessTest<JavaIOFile>(FileTestIO()) {
+    override fun getJavaFile(file: JavaIOFile): File {
+        return file.file
+    }
+
+    @Test
+    fun testReadUnreadableFile() = runTest {
+        // ensure the file exists by writing into it
+        testFile.file.writeText("")
+        testFile.file.setReadable(false)
+        val result = runCatching {
+            store.data.first()
+        }
+
+        Truth.assertThat(result.exceptionOrNull()).isInstanceOf(IOException::class.java)
+        Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains("Permission denied")
+    }
+
+    @Test
+    fun testReadAfterTransientBadRead() = runTest {
+        // ensure the file exists by writing into it
+        testFile.file.writeText("")
+        testFile.file.setReadable(false)
+
+        assertThrows<IOException> { store.data.first() }.hasMessageThat()
+            .contains("Permission denied")
+
+        testFile.file.setReadable(true)
+        Truth.assertThat(store.data.first()).isEqualTo(0)
+    }
+
+    @Test
+    fun testMutatingDataStoreFails() = runTest {
+
+        val dataStore = DataStoreImpl(
+            storage = FileStorage(ByteWrapper.ByteWrapperSerializer(), {
+                MultiProcessCoordinator(dataStoreScope.coroutineContext, it)
+            }) { testFile.file },
+            scope = dataStoreScope,
+        )
+
+        assertThrows<IllegalStateException> {
+            dataStore.updateData { input: ByteWrapper ->
+                // mutating our wrapper causes us to fail
+                input.byte = 123.toByte()
+                input
+            }
+        }
+    }
+
+    @Test
+    fun stressTest() = runBlocking {
+        val stressTestFile = getJavaFile(testIO.newTempFile(tempFolder))
+        val testJob = Job()
+        val testScope = CoroutineScope(
+            Dispatchers.IO + testJob
+        )
+        val stressTestStore = DataStoreImpl<Int>(
+            storage = FileStorage(
+                object : Serializer<Int> {
+                    override val defaultValue: Int
+                        get() = 0
+
+                    override suspend fun readFrom(input: InputStream): Int {
+                        return input.reader(Charsets.UTF_8).use {
+                            it.readText().toIntOrNull() ?: defaultValue
+                        }
+                    }
+
+                    override suspend fun writeTo(t: Int, output: OutputStream) {
+                        output.writer(Charsets.UTF_8).use {
+                            it.write(t.toString())
+                            it.flush()
+                        }
+                    }
+                },
+                coordinatorProducer = {
+                    MultiProcessCoordinator(testScope.coroutineContext, it)
+                },
+                produceFile = { stressTestFile }
+            ),
+            scope = testScope,
+            initTasksList = emptyList()
+        )
+        val limit = 1_000
+        stressTestStore.updateData { 0 }
+        val reader = async(Dispatchers.IO + testJob) {
+            stressTestStore.data.scan(0) { prev, next ->
+                check(next >= prev) {
+                    "check failed: $prev / $next"
+                }
+                next
+            }.take(limit - 200).collect() // we can drop some intermediate values, it is fine
+        }
+        val writer = async {
+            repeat(limit) {
+                stressTestStore.updateData {
+                    it + 1
+                }
+            }
+        }
+        listOf(reader, writer).awaitAll()
+        testJob.cancelAndJoin()
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessOkioTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessOkioTest.kt
new file mode 100644
index 0000000..d71d516
--- /dev/null
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessOkioTest.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.datastore.core
+
+import androidx.datastore.OkioPath
+import androidx.datastore.OkioTestIO
+import java.io.File
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.ObsoleteCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
+@InternalCoroutinesApi
+class MultiProcessDataStoreSingleProcessOkioTest :
+    MultiProcessDataStoreSingleProcessTest<OkioPath>(OkioTestIO()) {
+    override fun getJavaFile(file: OkioPath): File {
+        return file.path.toFile()
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
similarity index 83%
rename from datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreTest.kt
rename to datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
index cb0b049..0ffe3d0 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
@@ -17,6 +17,8 @@
 package androidx.datastore.core
 
 import android.os.StrictMode
+import androidx.datastore.TestFile
+import androidx.datastore.TestIO
 import androidx.datastore.TestingSerializerConfig
 import androidx.datastore.core.handlers.NoOpCorruptionHandler
 import androidx.test.filters.FlakyTest
@@ -24,7 +26,6 @@
 import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
 import java.io.File
-import java.io.IOException
 import java.io.InputStream
 import java.io.OutputStream
 import java.util.concurrent.Executors
@@ -32,6 +33,7 @@
 import java.util.concurrent.atomic.AtomicInteger
 import kotlin.coroutines.AbstractCoroutineContextElement
 import kotlin.coroutines.CoroutineContext
+import kotlin.random.Random
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
@@ -41,13 +43,10 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.take
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.job
@@ -59,9 +58,7 @@
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.withContext
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
@@ -73,28 +70,32 @@
 @ExperimentalCoroutinesApi
 @LargeTest
 @RunWith(JUnit4::class)
-class MultiProcessDataStoreTest {
-    @get:Rule
-    val tempFolder = TemporaryFolder()
-
-    private lateinit var store: DataStore<Byte>
+abstract class MultiProcessDataStoreSingleProcessTest<F : TestFile>(
+    protected val testIO: TestIO<F, *>
+) {
+    protected lateinit var store: DataStore<Byte>
     private lateinit var serializerConfig: TestingSerializerConfig
-    private lateinit var testingSerializer: TestingSerializer
-    private lateinit var testFile: File
-    private lateinit var dataStoreScope: TestScope
+    protected lateinit var testFile: F
+    protected lateinit var tempFolder: F
+    protected lateinit var dataStoreScope: TestScope
+
+    abstract fun getJavaFile(file: F): File
 
     private fun newDataStore(
-        file: File = testFile,
-        serializer: Serializer<Byte> = testingSerializer,
+        file: TestFile = testFile,
         scope: CoroutineScope = dataStoreScope,
         initTasksList: List<suspend (api: InitializerApi<Byte>) -> Unit> = listOf(),
         corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler<Byte>()
     ): DataStore<Byte> {
         return DataStoreImpl(
-            storage = FileStorage(
-                serializer,
-                { MultiProcessCoordinator(scope.coroutineContext, it) }
-            ) { file },
+            storage = testIO.getStorage(
+                serializerConfig,
+                {
+                    MultiProcessCoordinator(
+                        dataStoreScope.coroutineContext,
+                        getJavaFile(testFile)
+                    )
+                }) { file },
             scope = scope,
             initTasksList = initTasksList,
             corruptionHandler = corruptionHandler
@@ -104,15 +105,14 @@
     @Before
     fun setUp() {
         serializerConfig = TestingSerializerConfig()
-        testingSerializer = TestingSerializer(serializerConfig)
-        testFile = tempFolder.newFile()
+        tempFolder = testIO.tempDir()
+        testFile = testIO.newTempFile(tempFolder)
         dataStoreScope = TestScope(UnconfinedTestDispatcher() + Job())
-        store =
-            newDataStore(
-                testFile,
-                testingSerializer,
-                scope = dataStoreScope
-            )
+        store = testIO.getStore(
+            serializerConfig,
+            dataStoreScope,
+            { MultiProcessCoordinator(dataStoreScope.coroutineContext, getJavaFile(testFile)) }
+        ) { testFile }
     }
 
     @Test
@@ -133,28 +133,6 @@
     }
 
     @Test
-    fun testReadUnreadableFile() = runTest {
-        testFile.setReadable(false)
-        val result = runCatching {
-            store.data.first()
-        }
-
-        assertThat(result.exceptionOrNull()).isInstanceOf(IOException::class.java)
-        assertThat(result.exceptionOrNull()).hasMessageThat().contains("Permission denied")
-    }
-
-    @Test
-    fun testReadAfterTransientBadRead() = runTest {
-        testFile.setReadable(false)
-
-        assertThrows<IOException> { store.data.first() }.hasMessageThat()
-            .contains("Permission denied")
-
-        testFile.setReadable(true)
-        assertThat(store.data.first()).isEqualTo(0)
-    }
-
-    @Test
     fun testScopeCancelledWithActiveFlow() = runTest {
         val storeScope = CoroutineScope(Job())
         val dataStore = newDataStore(scope = storeScope)
@@ -215,7 +193,7 @@
 
     @Test
     fun testReadAfterTransientBadWrite() = runBlocking {
-        val file = tempFolder.newFile()
+        val file = testIO.newTempFile(tempFolder)
         runTest {
             val store = newDataStore(file, scope = backgroundScope)
             store.updateData { 1 }
@@ -231,8 +209,13 @@
 
     @Test
     fun testWriteToNonExistentDir() = runBlocking {
-        val fileInNonExistentDir =
-            File(tempFolder.newFolder(), "/this/does/not/exist/foo.tst")
+        val fileInNonExistentDir = testIO.newTempFile(
+            testIO.tempDir(
+                "/this/does/not/exist",
+                makeDirs = false,
+                parentDir = testIO.tempDir()
+            )
+        )
         runTest {
             val newStore = newDataStore(fileInNonExistentDir, scope = backgroundScope)
 
@@ -249,18 +232,16 @@
 
     @Test
     fun testReadFromNonExistentFile() = runTest {
-        val nonExistentFile = tempFolder.newFile()
-        assertThat(nonExistentFile.delete()).isTrue()
-        val newStore = newDataStore(nonExistentFile)
+        testFile.deleteIfExists()
+        val newStore = newDataStore(testFile)
         assertThat(newStore.data.first()).isEqualTo(0)
     }
 
     @Test
     fun testWriteToDirFails() = runTest {
-        val directoryFile =
-            File(tempFolder.newFolder(), "/this/is/a/directory")
-        directoryFile.mkdirs()
-        assertThat(directoryFile.isDirectory).isTrue()
+        val directoryFile = testIO.tempDir("/this/is/a${Random.nextInt()}/directory")
+
+        assertThat(testIO.isDirectory(directoryFile))
 
         val newStore = newDataStore(directoryFile)
         assertThrows<IOException> { newStore.data.first() }
@@ -277,14 +258,16 @@
             testFile
         }
 
-        val newStore = DataStoreImpl(
-            storage = FileStorage(
-                testingSerializer,
-                { MultiProcessCoordinator(dataStoreScope.coroutineContext, it) },
-                fileProducer
-            ),
-            scope = dataStoreScope,
-            initTasksList = listOf()
+        val newStore = testIO.getStore(
+            serializerConfig,
+            dataStoreScope,
+            {
+                MultiProcessCoordinator(
+                    dataStoreScope.coroutineContext,
+                    getJavaFile(fileProducer())
+                )
+            },
+            fileProducer
         )
 
         assertThrows<IOException> { newStore.data.first() }.hasMessageThat().isEqualTo(
@@ -315,6 +298,9 @@
 
     @Test
     fun testWriteAfterTransientBadRead() = runTest {
+        testFile.createIfNotExists()
+        assertThat(testFile.exists()).isTrue()
+
         serializerConfig.failingRead = true
 
         assertThrows<IOException> { store.data.first() }
@@ -327,6 +313,9 @@
 
     @Test
     fun testWriteWithBadReadFails() = runTest {
+        testFile.createIfNotExists()
+        assertThat(testFile.exists()).isTrue()
+
         serializerConfig.failingRead = true
 
         assertThrows<IOException> { store.updateData { 1 } }
@@ -624,61 +613,6 @@
     }
 
     @Test
-    fun stressTest() = runBlocking {
-        val stressTestFile = tempFolder.newFile()
-        val testJob = Job()
-        val testScope = CoroutineScope(
-            Dispatchers.IO + testJob
-        )
-        val stressTestStore = DataStoreImpl<Int>(
-            storage = FileStorage(
-                object : Serializer<Int> {
-                    override val defaultValue: Int
-                        get() = 0
-
-                    override suspend fun readFrom(input: InputStream): Int {
-                        return input.reader(Charsets.UTF_8).use {
-                            it.readText().toIntOrNull() ?: defaultValue
-                        }
-                    }
-
-                    override suspend fun writeTo(t: Int, output: OutputStream) {
-                        output.writer(Charsets.UTF_8).use {
-                            it.write(t.toString())
-                            it.flush()
-                        }
-                    }
-                },
-                coordinatorProducer = {
-                    MultiProcessCoordinator(testScope.coroutineContext, it)
-                },
-                produceFile = { stressTestFile }
-            ),
-            scope = testScope,
-            initTasksList = emptyList()
-        )
-        val limit = 1_000
-        stressTestStore.updateData { 0 }
-        val reader = async(Dispatchers.IO + testJob) {
-            stressTestStore.data.scan(0) { prev, next ->
-                check(next >= prev) {
-                    "check failed: $prev / $next"
-                }
-                next
-            }.take(limit - 200).collect() // we can drop some intermediate values, it is fine
-        }
-        val writer = async {
-            repeat(limit) {
-                stressTestStore.updateData {
-                    it + 1
-                }
-            }
-        }
-        listOf(reader, writer).awaitAll()
-        testJob.cancelAndJoin()
-    }
-
-    @Test
     fun testMultipleFlowsReceiveData() = runTest {
         val flowOf8 = store.data.take(8)
 
@@ -857,37 +791,15 @@
     }
 
     @Test
-    fun testMutatingDataStoreFails() = runTest {
-
-        val dataStore = DataStoreImpl(
-            storage = FileStorage(ByteWrapper.ByteWrapperSerializer(), {
-                MultiProcessCoordinator(dataStoreScope.coroutineContext, it)
-            }) { testFile },
-            scope = dataStoreScope,
-        )
-
-        assertThrows<IllegalStateException> {
-            dataStore.updateData { input: ByteWrapper ->
-                // mutating our wrapper causes us to fail
-                input.byte = 123.toByte()
-                input
-            }
-        }
-    }
-
-    @Test
     fun testDefaultValueUsedWhenNoDataOnDisk() = runTest {
-        val dataStore = DataStoreImpl(
-            storage = FileStorage(
-                TestingSerializer(TestingSerializerConfig(defaultValue = 99)),
-                { MultiProcessCoordinator(dataStoreScope.coroutineContext, it) }
-            ) {
-                testFile
-            },
-            scope = dataStoreScope
-        )
+        testFile.deleteIfExists()
 
-        assertThat(testFile.delete()).isTrue()
+        val dataStore = testIO.getStore(
+            TestingSerializerConfig(defaultValue = 99),
+            dataStoreScope,
+            { MultiProcessCoordinator(dataStoreScope.coroutineContext, getJavaFile(testFile)) }) {
+            testFile
+        }
 
         assertThat(dataStore.data.first()).isEqualTo(99)
     }
@@ -983,7 +895,7 @@
 
     @Test
     fun testCreateDuplicateActiveDataStore() = runTest {
-        val file = tempFolder.newFile()
+        val file = testIO.newTempFile(tempFolder)
         val dataStore = newDataStore(file = file, scope = CoroutineScope(Job()))
 
         dataStore.data.first()
@@ -997,7 +909,7 @@
 
     @Test
     fun testCreateDataStore_withSameFileAsInactiveDataStore() = runTest {
-        val file = tempFolder.newFile()
+        val file = testIO.newTempFile(tempFolder)
         val scope1 = CoroutineScope(Job())
         val dataStore1 = newDataStore(file = file, scope = scope1)
 
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/ProtoOkioSerializer.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/ProtoOkioSerializer.kt
new file mode 100644
index 0000000..29540b5
--- /dev/null
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/ProtoOkioSerializer.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.datastore.core
+
+import androidx.datastore.core.okio.OkioSerializer
+import com.google.protobuf.ExtensionRegistryLite
+import com.google.protobuf.InvalidProtocolBufferException
+import com.google.protobuf.MessageLite
+import okio.BufferedSink
+import okio.BufferedSource
+
+/** OkioSerializer for using DataStore with protos. */
+internal class ProtoOkioSerializer<T : MessageLite>(
+    /** The default proto of this type, obtained via {@code T.getDefaultInstance()} */
+    override val defaultValue: T,
+    /**
+     *  Set the extensionRegistryLite to use when deserializing T. If no extension registry is
+     *  necessary, use {@code ExtensionRegistryLite.getEmptyRegistry()}.
+     */
+    private val extensionRegistryLite: ExtensionRegistryLite =
+        ExtensionRegistryLite.getEmptyRegistry()
+) : OkioSerializer<T> {
+    @Suppress("UNCHECKED_CAST")
+    override suspend fun readFrom(source: BufferedSource): T {
+        try {
+            return defaultValue.parserForType.parseFrom(
+                source.inputStream(),
+                extensionRegistryLite
+            ) as T
+        } catch (invalidProtocolBufferException: InvalidProtocolBufferException) {
+            throw CorruptionException(
+                "Cannot read proto.", invalidProtocolBufferException
+            )
+        }
+    }
+
+    override suspend fun writeTo(t: T, sink: BufferedSink) {
+        t.writeTo(sink.outputStream())
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
index d30f1d9..ccc6edb 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
@@ -143,7 +143,8 @@
         )
 
         val storage = testIO.getStorage(
-            TestingSerializerConfig(failingWrite = true)
+            TestingSerializerConfig(failingWrite = true),
+            { createSingleProcessCoordinator() }
         ) { testIO.newTempFile() }
         val store = newDataStore(
             initTasksList = listOf(
@@ -194,7 +195,8 @@
     private fun newDataStore(
         initTasksList: List<suspend (api: InitializerApi<Byte>) -> Unit> = listOf(),
         storage: Storage<Byte> = testIO.getStorage(
-            TestingSerializerConfig()
+            TestingSerializerConfig(),
+            { createSingleProcessCoordinator() }
         )
     ): DataStore<Byte> {
         return DataStoreImpl(
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
index 898badd..dfa3273 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
@@ -68,7 +68,11 @@
         tempFolder = testIO.tempDir()
         testFile = testIO.newTempFile(tempFolder)
         dataStoreScope = TestScope(UnconfinedTestDispatcher())
-        store = testIO.getStore(serializerConfig, dataStoreScope) { testFile }
+        store = testIO.getStore(
+            serializerConfig,
+            dataStoreScope,
+            { createSingleProcessCoordinator() }
+        ) { testFile }
     }
 
     fun doTest(initDataStore: Boolean = false, test: suspend TestScope.() -> Unit) {
@@ -102,7 +106,10 @@
     @Test
     fun testScopeCancelledWithActiveFlow() = doTest {
         val storeScope = CoroutineScope(Job())
-        val store = testIO.getStore(serializerConfig, storeScope) { testFile }
+        val store = testIO.getStore(
+            serializerConfig,
+            storeScope,
+            { createSingleProcessCoordinator() }) { testFile }
 
         val collection = async {
             store.data.take(2).collect {
@@ -175,7 +182,8 @@
     @Test
     fun testWriteToNonExistentDir() = doTest {
         val fileInNonExistentDir = testIO.newTempFile(
-            testIO.tempDir("/this/does/not/exist", makeDirs = false))
+            testIO.tempDir("/this/does/not/exist", makeDirs = false)
+        )
 
         coroutineScope {
             val newStore = newDataStore(fileInNonExistentDir, scope = this)
@@ -193,6 +201,7 @@
 
     @Test
     fun testReadFromNonExistentFile() = doTest {
+        // TODO remove deleteIfExists after b/276983736
         testFile.deleteIfExists()
         val newStore = newDataStore(testFile)
         assertThat(newStore.data.first()).isEqualTo(0)
@@ -218,7 +227,12 @@
             }
             testFile
         }
-        val newStore = testIO.getStore(serializerConfig, dataStoreScope, fileProducer)
+        val newStore = testIO.getStore(
+            serializerConfig,
+            dataStoreScope,
+            { createSingleProcessCoordinator() },
+            fileProducer
+        )
 
         assertThrows<IOException> { newStore.data.first() }.hasMessageThat().isEqualTo(
             "Exception when producing file"
@@ -692,7 +706,9 @@
         testFile.deleteIfExists()
         val dataStore = newDataStore(
             serializerConfig = TestingSerializerConfig(defaultValue = 99),
-            scope = dataStoreScope)
+            scope = dataStoreScope
+        )
+
         assertThat(dataStore.data.first()).isEqualTo(99)
     }
 
@@ -876,6 +892,7 @@
             file = testIO.newTempFile(),
             scope = datastoreScope.backgroundScope
         )
+
         suspend fun <R> runAndPumpInStore(block: suspend () -> R): R {
             val async = datastoreScope.async { block() }
             datastoreScope.runCurrent()
@@ -957,7 +974,7 @@
         corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler<Byte>()
     ): DataStore<Byte> {
         return DataStoreImpl(
-            testIO.getStorage(serializerConfig) { file },
+            testIO.getStorage(serializerConfig, { createSingleProcessCoordinator() }) { file },
             scope = scope,
             initTasksList = initTasksList,
             corruptionHandler = corruptionHandler
diff --git a/datastore/datastore-core/src/jvmMain/kotlin/androidx/datastore/core/FileStorage.kt b/datastore/datastore-core/src/jvmMain/kotlin/androidx/datastore/core/FileStorage.kt
index 91e35d0..ace5688 100644
--- a/datastore/datastore-core/src/jvmMain/kotlin/androidx/datastore/core/FileStorage.kt
+++ b/datastore/datastore-core/src/jvmMain/kotlin/androidx/datastore/core/FileStorage.kt
@@ -32,6 +32,9 @@
  * file location.
  *
  * @param serializer The serializer that can write <T> to and from a byte array.
+ * @param coordinatorProducer The producer to provide [InterProcessCoordinator] that coordinates IO
+ * operations across processes if needed. By default it provides single process coordinator, which
+ * doesn't support cross process use cases.
  * @param produceFile The file producer that returns the file that will be read and written.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
diff --git a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
index b365438..033a8c9 100644
--- a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
+++ b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
@@ -37,7 +37,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @InternalCoroutinesApi
-class SingleProcessDatastoreJavaTest : SingleProcessDataStoreTest<JavaIOFile>(FileTestIO()) {
+class SingleProcessDataStoreJavaTest : SingleProcessDataStoreTest<JavaIOFile>(FileTestIO()) {
 
     @Test
     fun testMutatingDataStoreFails() = doTest {
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index ae580c2..66b74fc0 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -8,10 +8,10 @@
 }
 
 dependencies {
-    docs("androidx.activity:activity:1.8.0-alpha02")
-    docs("androidx.activity:activity-compose:1.8.0-alpha02")
-    samples("androidx.activity:activity-compose-samples:1.8.0-alpha02")
-    docs("androidx.activity:activity-ktx:1.8.0-alpha02")
+    docs("androidx.activity:activity:1.8.0-alpha03")
+    docs("androidx.activity:activity-compose:1.8.0-alpha03")
+    samples("androidx.activity:activity-compose-samples:1.8.0-alpha03")
+    docs("androidx.activity:activity-ktx:1.8.0-alpha03")
     docs("androidx.ads:ads-identifier:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-common:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-provider:1.0.0-alpha05")
@@ -120,12 +120,12 @@
     docs("androidx.core:core-role:1.2.0-alpha01")
     docs("androidx.core:core-animation:1.0.0-beta02")
     docs("androidx.core:core-animation-testing:1.0.0-beta01")
-    docs("androidx.core:core:1.12.0-alpha01")
-    docs("androidx.core:core-ktx:1.12.0-alpha01")
+    docs("androidx.core:core:1.12.0-alpha03")
+    docs("androidx.core:core-ktx:1.12.0-alpha03")
     docs("androidx.core:core-splashscreen:1.1.0-alpha01")
-    docs("androidx.core:core-testing:1.11.0-alpha02")
-    docs("androidx.credentials:credentials:1.2.0-alpha02")
-    docs("androidx.credentials:credentials-play-services-auth:1.2.0-alpha02")
+    docs("androidx.core:core-testing:1.12.0-alpha03")
+    docs("androidx.credentials:credentials:1.2.0-alpha03")
+    docs("androidx.credentials:credentials-play-services-auth:1.2.0-alpha03")
     docs("androidx.credentials:credentials-provider:1.0.0-alpha03")
     docs("androidx.cursoradapter:cursoradapter:1.0.0")
     docs("androidx.customview:customview:1.2.0-alpha02")
@@ -236,8 +236,8 @@
     docs("androidx.media3:media3-transformer:1.0.0")
     docs("androidx.media3:media3-ui:1.0.0")
     docs("androidx.media3:media3-ui-leanback:1.0.0")
-    docs("androidx.mediarouter:mediarouter:1.6.0-alpha02")
-    docs("androidx.mediarouter:mediarouter-testing:1.6.0-alpha02")
+    docs("androidx.mediarouter:mediarouter:1.6.0-alpha03")
+    docs("androidx.mediarouter:mediarouter-testing:1.6.0-alpha03")
     docs("androidx.metrics:metrics-performance:1.0.0-alpha04")
     docs("androidx.navigation:navigation-common:2.6.0-alpha09")
     docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha09")
diff --git a/fragment/fragment-ktx/api/1.6.0-beta01.txt b/fragment/fragment-ktx/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/1.6.0-beta01.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-beta01.txt b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta01.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta01.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-beta01.txt b/fragment/fragment-ktx/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-ktx/api/res-1.6.0-beta01.txt
diff --git a/fragment/fragment-ktx/api/restricted_1.6.0-beta01.txt b/fragment/fragment-ktx/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/restricted_1.6.0-beta01.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-beta01.txt b/fragment/fragment-testing-manifest/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/1.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta01.txt b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/res-1.6.0-beta01.txt b/fragment/fragment-testing-manifest/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/res-1.6.0-beta01.txt
diff --git a/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta01.txt b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing/api/1.6.0-beta01.txt b/fragment/fragment-testing/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/1.6.0-beta01.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-beta01.txt b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta01.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta01.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-beta01.txt b/fragment/fragment-testing/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing/api/res-1.6.0-beta01.txt
diff --git a/fragment/fragment-testing/api/restricted_1.6.0-beta01.txt b/fragment/fragment-testing/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/restricted_1.6.0-beta01.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/build.gradle b/fragment/fragment-testing/build.gradle
index 7afdb67..4ef2ed4 100644
--- a/fragment/fragment-testing/build.gradle
+++ b/fragment/fragment-testing/build.gradle
@@ -25,7 +25,7 @@
 
 dependencies {
     api(project(":fragment:fragment-ktx"))
-    api(libs.testCore)
+    api("androidx.test:core:1.5.0")
     api(libs.kotlinStdlib)
     api(project(":fragment:fragment-testing-manifest"))
     androidTestImplementation(libs.kotlinStdlib)
diff --git a/fragment/fragment/api/1.6.0-beta01.txt b/fragment/fragment/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..2f167cf
--- /dev/null
+++ b/fragment/fragment/api/1.6.0-beta01.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-beta01.txt b/fragment/fragment/api/public_plus_experimental_1.6.0-beta01.txt
new file mode 100644
index 0000000..2f167cf
--- /dev/null
+++ b/fragment/fragment/api/public_plus_experimental_1.6.0-beta01.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-beta01.txt b/fragment/fragment/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment/api/res-1.6.0-beta01.txt
diff --git a/fragment/fragment/api/restricted_1.6.0-beta01.txt b/fragment/fragment/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..83801ad
--- /dev/null
+++ b/fragment/fragment/api/restricted_1.6.0-beta01.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/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
index d3e0053..ef658d4 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
@@ -637,6 +637,8 @@
 
     private fun isSupported(): Boolean =
         // See "b/277225133" these tests pass on cuttlefish + other devices but fail for some reason
-        // FTL configured API level 33 emulator instanceson
-        !(Build.MODEL.contains("gphone") && Build.VERSION.SDK_INT == 33)
+        // 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)
 }
\ No newline at end of file
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index 47a9eac..8a3832e 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -25,6 +25,7 @@
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.type.ArtifactTypeDefinition
 import org.gradle.api.attributes.Attribute
 import org.gradle.api.tasks.StopExecutionException
 import org.gradle.kotlin.dsl.apply
@@ -64,6 +65,7 @@
             it.isCanBeConsumed = true
             it.isCanBeResolved = false
             it.extendsFrom(project.configurations.getByName("implementation"))
+            it.setupReleaseAttribute()
         }
 
         project.pluginManager.withPlugin("com.android.library") {
@@ -199,7 +201,9 @@
         }
     }
 
-    libraryProject.configurations.create(IMPORT_INSPECTOR_DEPENDENCIES)
+    libraryProject.configurations.create(IMPORT_INSPECTOR_DEPENDENCIES) {
+        it.setupReleaseAttribute()
+    }
     libraryProject.dependencies.add(IMPORT_INSPECTOR_DEPENDENCIES,
         libraryProject.dependencies.project(
             mapOf(
@@ -232,6 +236,25 @@
     }
 }
 
+private fun Configuration.setupReleaseAttribute() {
+    attributes {
+        it.attribute(
+            Attribute.of(
+                "com.android.build.api.attributes.BuildTypeAttr",
+                String::class.java
+            ),
+            "release"
+        )
+        it.attribute(
+            Attribute.of(
+                "artifactType",
+                String::class.java
+            ),
+            ArtifactTypeDefinition.JAR_TYPE
+        )
+    }
+}
+
 @ExperimentalStdlibApi
 private fun generateProguardDetectionFile(libraryProject: Project) {
     val libExtension = libraryProject.extensions.getByType(LibraryExtension::class.java)
diff --git a/libraryversions.toml b/libraryversions.toml
index 62c41c1..5f29e48 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -20,7 +20,7 @@
 CAR_APP = "1.4.0-alpha01"
 COLLECTION = "1.3.0-alpha05"
 COMPOSE = "1.5.0-alpha03"
-COMPOSE_COMPILER = "1.4.4"
+COMPOSE_COMPILER = "1.4.5"
 COMPOSE_MATERIAL3 = "1.1.0-rc01"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
@@ -55,7 +55,7 @@
 EMOJI2 = "1.4.0-beta02"
 ENTERPRISE = "1.1.0-rc01"
 EXIFINTERFACE = "1.4.0-alpha01"
-FRAGMENT = "1.6.0-alpha10"
+FRAGMENT = "1.6.0-beta01"
 FUTURES = "1.2.0-alpha01"
 GLANCE = "1.0.0-alpha06"
 GLANCE_TEMPLATE = "1.0.0-alpha01"
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
index eb44a5e..6601791 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2Test.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -63,7 +64,7 @@
 
     private Context mContext;
     private MediaRouter mRouter;
-    private MediaRouter.Callback mPlaceholderCallback = new MediaRouter.Callback() { };
+    private MediaRouter.Callback mPlaceholderCallback = new MediaRouter.Callback() {};
     StubMediaRouteProviderService mService;
     StubMediaRouteProviderService.StubMediaRouteProvider mProvider;
     MediaRouteProviderService.MediaRouteProviderServiceImplApi30 mServiceImpl;
@@ -117,14 +118,22 @@
 
     @After
     public void tearDown() {
-        getInstrumentation().runOnMainSync(() -> {
-            mRouter.removeCallback(mPlaceholderCallback);
-            for (MediaRouter.Callback callback : mCallbacks) {
-                mRouter.removeCallback(callback);
-            }
-            mCallbacks.clear();
-            MediaRouterTestHelper.resetMediaRouter();
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            for (RoutingSessionInfo sessionInfo :
+                                    mMr2ProviderServiceAdapter.getAllSessionInfo()) {
+                                mMr2ProviderServiceAdapter.onReleaseSession(
+                                        MediaRoute2ProviderService.REQUEST_ID_NONE,
+                                        sessionInfo.getId());
+                            }
+                            mRouter.removeCallback(mPlaceholderCallback);
+                            for (MediaRouter.Callback callback : mCallbacks) {
+                                mRouter.removeCallback(callback);
+                            }
+                            mCallbacks.clear();
+                            MediaRouterTestHelper.resetMediaRouter();
+                        });
         MediaRouter2TestActivity.finishActivity();
     }
 
@@ -191,6 +200,28 @@
 
     @SmallTest
     @Test
+    public void setRouteVolume_onStaticNonGroupRoute() {
+        // We run session creation on the main thread to ensure the route creation from the setup
+        // method happens before the session creation. Otherwise, this call may call into an
+        // inconsistent adapter state.
+        getInstrumentation()
+                .runOnMainSync(
+                        () ->
+                                mMr2ProviderServiceAdapter.onCreateSession(
+                                        MediaRoute2ProviderService.REQUEST_ID_NONE,
+                                        mContext.getPackageName(),
+                                        StubMediaRouteProviderService.ROUTE_ID1,
+                                        /* sessionHints= */ null));
+        StubMediaRouteProviderService.StubMediaRouteProvider.StubRouteController createdController =
+                mProvider.mControllers.get(StubMediaRouteProviderService.ROUTE_ID1);
+        assertNotNull(createdController); // Avoids nullability warning.
+        assertNull(createdController.mLastSetVolume);
+        mMr2ProviderServiceAdapter.setRouteVolume(StubMediaRouteProviderService.ROUTE_ID1, 100);
+        assertEquals(100, (int) createdController.mLastSetVolume);
+    }
+
+    @SmallTest
+    @Test
     public void onBinderDied_releaseRoutingSessions() throws Exception {
         String descriptorId = StubMediaRouteProviderService.ROUTE_ID1;
 
@@ -208,8 +239,9 @@
 
         try {
             List<Messenger> messengers =
-                    mServiceImpl.mClients.stream().map(client -> client.mMessenger)
-                    .collect(Collectors.toList());
+                    mServiceImpl.mClients.stream()
+                            .map(client -> client.mMessenger)
+                            .collect(Collectors.toList());
             getInstrumentation().runOnMainSync(() ->
                     messengers.forEach(mServiceImpl::onBinderDied));
             // It should have no session info.
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
index a0f60b4..97a2e83 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/StubMediaRouteProviderService.java
@@ -21,6 +21,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.collection.ArrayMap;
 
 import java.util.ArrayList;
@@ -82,6 +83,7 @@
 
     class StubMediaRouteProvider extends MediaRouteProvider {
         Map<String, MediaRouteDescriptor> mRoutes = new ArrayMap<>();
+        Map<String, StubRouteController> mControllers = new ArrayMap<>();
         boolean mSupportsDynamicGroup = false;
 
         StubMediaRouteProvider(@NonNull Context context) {
@@ -90,7 +92,9 @@
 
         @Override
         public RouteController onCreateRouteController(@NonNull String routeId) {
-            return new StubRouteController(routeId);
+            StubRouteController newController = new StubRouteController(routeId);
+            mControllers.put(routeId, newController);
+            return newController;
         }
 
         public void initializeRoutes() {
@@ -114,6 +118,8 @@
         //TODO: Implement DynamicGroupRouteController
         class StubRouteController extends RouteController {
             final String mRouteId;
+            @Nullable Integer mLastSetVolume;
+
             StubRouteController(String routeId) {
                 mRouteId = routeId;
             }
@@ -133,6 +139,11 @@
                         .build());
                 publishRoutes();
             }
+
+            @Override
+            public void onSetVolume(int volume) {
+                mLastSetVolume = volume;
+            }
         }
     }
 }
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
index d5381f5..53c71e5 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
@@ -177,13 +177,20 @@
         RoutingSessionInfo sessionInfo = builder.build();
         sessionRecord.setSessionInfo(sessionInfo);
 
-        // Create member route controllers if it's a static group. Member route controllers
-        // for a dynamic group will be created after the group route is created.
-        // (DynamicGroupRouteController#notifyDynamicRoutesChanged is called)
-        if ((sessionFlags & (SessionRecord.SESSION_FLAG_GROUP | SessionRecord.SESSION_FLAG_DYNAMIC))
-                == SessionRecord.SESSION_FLAG_GROUP) {
-            sessionRecord.updateMemberRouteControllers(routeId, /*oldSession=*/null,
-                    sessionInfo);
+        if ((sessionFlags & SessionRecord.SESSION_FLAG_DYNAMIC) == 0) {
+            if ((sessionFlags & SessionRecord.SESSION_FLAG_GROUP) != 0) {
+                // Create member route controllers if it's a static group. Member route controllers
+                // for a dynamic group will be created after the group route is created.
+                // (DynamicGroupRouteController#notifyDynamicRoutesChanged is called).
+                sessionRecord.updateMemberRouteControllers(
+                        routeId, /* oldSession= */ null, sessionInfo);
+            } else {
+                // The session has a non-group static route controller, whose proxy route
+                // controller has already been created. We just need to map the route id to said
+                // controller, for the controller to be found by its corresponding route id via
+                // findControllerByRouteId (needed, for example, for route volume adjustment).
+                sessionRecord.setStaticMemberRouteId(routeId);
+            }
         }
 
         mServiceImpl.setDynamicRoutesChangedListener(controller);
@@ -665,6 +672,19 @@
             mClientRecord = new WeakReference<>(clientRecord);
         }
 
+        /**
+         * Maps the provided {@code routeId} to the top level route controller of this session.
+         *
+         * <p>This method can be used for mapping a route id to a non-group static route controller.
+         * The session record takes care of the creation of the member route controllers, but not of
+         * the top level route controller, which is provided via the constructor. In the case of
+         * non-group static routes, the top level route controller is the single route controller,
+         * and has already been created, so we only need to map the corresponding route id to it.
+         */
+        public void setStaticMemberRouteId(String routeId) {
+            mRouteIdToControllerMap.put(routeId, mController);
+        }
+
         public int getFlags() {
             return mFlags;
         }
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 306f1ab..476ad28 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,6 +25,6 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=9848933
+androidx.playground.snapshotBuildId=9912543
 androidx.playground.metalavaBuildId=9883460
 androidx.studio.type=playground
diff --git a/privacysandbox/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index ce65d8c..c17bdce 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -46,7 +46,7 @@
             include: "android.jar"
     ))
     // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.BUILD_TOOLS_VERSION}/aidl"
+    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
     def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
     test {
         inputs.files(aidlCompilerPath)
diff --git a/privacysandbox/tools/tools-apigenerator/build.gradle b/privacysandbox/tools/tools-apigenerator/build.gradle
index b509312..afe4b88 100644
--- a/privacysandbox/tools/tools-apigenerator/build.gradle
+++ b/privacysandbox/tools/tools-apigenerator/build.gradle
@@ -53,7 +53,7 @@
             include: "android.jar"
     ))
     // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.BUILD_TOOLS_VERSION}/aidl"
+    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
     def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
     test {
         inputs.files(aidlCompilerPath)
diff --git a/privacysandbox/tools/tools-core/build.gradle b/privacysandbox/tools/tools-core/build.gradle
index a1b06db..7f2ce6b 100644
--- a/privacysandbox/tools/tools-core/build.gradle
+++ b/privacysandbox/tools/tools-core/build.gradle
@@ -43,7 +43,7 @@
             include: "android.jar"
     ))
     // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.BUILD_TOOLS_VERSION}/aidl"
+    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
     def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
     test {
         inputs.files(aidlCompilerPath)
diff --git a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationOnGeneratedProfiles.kt b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationOnGeneratedProfiles.kt
index a350bf7..0516ad7 100644
--- a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationOnGeneratedProfiles.kt
+++ b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationOnGeneratedProfiles.kt
@@ -54,6 +54,9 @@
 
     @Before
     fun setUp() {
+        // TODO: to re-enable for api 34 (b/276970167)
+        Assume.assumeTrue(!isApi34)
+
         // Note that this test fails on emulator api 30 (b/251540646)
         Assume.assumeTrue(!isApi30)
         withPackageName(packageName) { uninstall() }
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
index 7543f34..4fbbfbb 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
@@ -117,14 +117,29 @@
             return false;
         }
 
-        if (!mCurProfile.canWrite()) {
-            // It's possible that some OEMs might not allow writing to this directory. If this is
-            // the case, there's not really anything we can do, so we should quit before doing
-            // any unnecessary work.
-            result(ProfileInstaller.RESULT_NOT_WRITABLE, null);
-            return false;
+        // Check if the current profile file can be written. In Android U the current profile is
+        // no more created empty at app startup, so we need to deal with both file already existing
+        // and not existing. When the file exists, we just want to make sure that it's writeable.
+        // When the file does not exist, we want to make sure that it can be created.
+        // If this is not possible on the device, there is nothing we can do. This behavior might
+        // also be customized by OEM, that could prevent writing this file.
+        if (mCurProfile.exists()) {
+            if (!mCurProfile.canWrite()) {
+                result(ProfileInstaller.RESULT_NOT_WRITABLE, null);
+                return false;
+            }
+        } else {
+            try {
+                mCurProfile.createNewFile();
+            } catch (IOException e) {
+                // If the file cannot be created it's the same of the profile file not being
+                // writeable
+                result(ProfileInstaller.RESULT_NOT_WRITABLE, null);
+                return false;
+            }
         }
 
+
         mDeviceSupportsAotProfile = true;
         return true;
     }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/result/PreparedQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/result/PreparedQueryResultAdapter.kt
index 7d6a09d..18406e2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/result/PreparedQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/result/PreparedQueryResultAdapter.kt
@@ -73,6 +73,9 @@
             } else {
                 "executeUpdateDelete"
             }
+            if (preparedStmtProperty != null) {
+                beginControlFlow("try")
+            }
             addStatement("%N.beginTransaction()", dbProperty)
             beginControlFlow("try").apply {
                 if (returnType.isVoid() || returnType.isVoidObject() || returnType.isKotlinUnit()) {
@@ -97,11 +100,13 @@
             }
             nextControlFlow("finally").apply {
                 addStatement("%N.endTransaction()", dbProperty)
-                if (preparedStmtProperty != null) {
-                    addStatement("%N.release(%L)", preparedStmtProperty, stmtQueryVal)
-                }
             }
             endControlFlow()
+            if (preparedStmtProperty != null) {
+                nextControlFlow("finally")
+                addStatement("%N.release(%L)", preparedStmtProperty, stmtQueryVal)
+                endControlFlow()
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
index 58b7677..598d267 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
@@ -287,13 +287,16 @@
         final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
         try {
-            final int _result = _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
-            return _result;
+            __db.beginTransaction();
+            try {
+                final int _result = _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+                return _result;
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfDeleteByUid.release(_stmt);
         }
     }
@@ -307,13 +310,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return null;
+                    __db.beginTransaction();
+                    try {
+                        _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return null;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -329,13 +335,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -351,13 +360,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -368,13 +380,16 @@
     public int deleteEverything() {
         __db.assertNotSuspendingTransaction();
         final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteEverything.acquire();
-        __db.beginTransaction();
         try {
-            final int _result = _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
-            return _result;
+            __db.beginTransaction();
+            try {
+                final int _result = _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+                return _result;
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfDeleteEverything.release(_stmt);
         }
     }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
index 8873083..e3b6103 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
@@ -363,12 +363,15 @@
         } else {
             _stmt.bindString(_argIndex, uid);
         }
-        __db.beginTransaction();
         try {
-            _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
+            __db.beginTransaction();
+            try {
+                _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfAgeUserByUid.release(_stmt);
         }
     }
@@ -377,12 +380,15 @@
     public void ageUserAll() {
         __db.assertNotSuspendingTransaction();
         final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-        __db.beginTransaction();
         try {
-            _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
+            __db.beginTransaction();
+            try {
+                _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfAgeUserAll.release(_stmt);
         }
     }
@@ -394,13 +400,16 @@
             @Nullable
             public Void call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return null;
+                    __db.beginTransaction();
+                    try {
+                        _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return null;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
@@ -414,13 +423,16 @@
             @Nullable
             public Integer call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
@@ -434,13 +446,16 @@
             @Nullable
             public Integer call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
index a8478f5..4fa7155 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
@@ -281,13 +281,16 @@
         final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
         try {
-            final int _result = _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
-            return _result;
+            __db.beginTransaction();
+            try {
+                final int _result = _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+                return _result;
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfDeleteByUid.release(_stmt);
         }
     }
@@ -301,13 +304,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return null;
+                    __db.beginTransaction();
+                    try {
+                        _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return null;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -323,13 +329,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -345,13 +354,16 @@
                 final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
                 int _argIndex = 1;
                 _stmt.bindLong(_argIndex, uid);
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfDeleteByUid.release(_stmt);
                 }
             }
@@ -362,13 +374,16 @@
     public int deleteEverything() {
         __db.assertNotSuspendingTransaction();
         final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteEverything.acquire();
-        __db.beginTransaction();
         try {
-            final int _result = _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
-            return _result;
+            __db.beginTransaction();
+            try {
+                final int _result = _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+                return _result;
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfDeleteEverything.release(_stmt);
         }
     }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
index a17d135..571e245 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
@@ -330,12 +330,15 @@
         final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserByUid.acquire();
         int _argIndex = 1;
         _stmt.bindString(_argIndex, uid);
-        __db.beginTransaction();
         try {
-            _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
+            __db.beginTransaction();
+            try {
+                _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfAgeUserByUid.release(_stmt);
         }
     }
@@ -344,12 +347,15 @@
     public void ageUserAll() {
         __db.assertNotSuspendingTransaction();
         final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-        __db.beginTransaction();
         try {
-            _stmt.executeUpdateDelete();
-            __db.setTransactionSuccessful();
+            __db.beginTransaction();
+            try {
+                _stmt.executeUpdateDelete();
+                __db.setTransactionSuccessful();
+            } finally {
+                __db.endTransaction();
+            }
         } finally {
-            __db.endTransaction();
             __preparedStmtOfAgeUserAll.release(_stmt);
         }
     }
@@ -361,13 +367,16 @@
             @Nullable
             public Void call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return null;
+                    __db.beginTransaction();
+                    try {
+                        _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return null;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
@@ -381,13 +390,16 @@
             @Nullable
             public Integer call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
@@ -401,13 +413,16 @@
             @Nullable
             public Integer call() throws Exception {
                 final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-                __db.beginTransaction();
                 try {
-                    final Integer _result = _stmt.executeUpdateDelete();
-                    __db.setTransactionSuccessful();
-                    return _result;
+                    __db.beginTransaction();
+                    try {
+                        final Integer _result = _stmt.executeUpdateDelete();
+                        __db.setTransactionSuccessful();
+                        return _result;
+                    } finally {
+                        __db.endTransaction();
+                    }
                 } finally {
-                    __db.endTransaction();
                     __preparedStmtOfAgeUserAll.release(_stmt);
                 }
             }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
index c2562be..e7ff72b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
@@ -38,13 +38,16 @@
         val _stmt: SupportSQLiteStatement = __preparedStmtOfInsertEntity.acquire()
         var _argIndex: Int = 1
         _stmt.bindLong(_argIndex, id)
-        __db.beginTransaction()
         try {
-            val _result: Long = _stmt.executeInsert()
-            __db.setTransactionSuccessful()
-            return _result
+            __db.beginTransaction()
+            try {
+                val _result: Long = _stmt.executeInsert()
+                __db.setTransactionSuccessful()
+                return _result
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfInsertEntity.release(_stmt)
         }
     }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
index eeee3c0..9bbf6ad 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
@@ -41,13 +41,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    val _result: Long? = _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return _result
+                    __db.beginTransaction()
+                    try {
+                        val _result: Long? = _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return _result
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
@@ -61,13 +64,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    val _result: Long? = _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return _result
+                    __db.beginTransaction()
+                    try {
+                        val _result: Long? = _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return _result
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
@@ -81,13 +87,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return null
+                    __db.beginTransaction()
+                    try {
+                        _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return null
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
index f6d72eb..81bb13a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
@@ -41,13 +41,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    val _result: Long? = _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return _result
+                    __db.beginTransaction()
+                    try {
+                        val _result: Long? = _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return _result
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
@@ -61,13 +64,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    val _result: Long? = _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return _result
+                    __db.beginTransaction()
+                    try {
+                        val _result: Long? = _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return _result
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
@@ -81,13 +87,16 @@
                 _stmt.bindString(_argIndex, id)
                 _argIndex = 2
                 _stmt.bindString(_argIndex, name)
-                __db.beginTransaction()
                 try {
-                    _stmt.executeInsert()
-                    __db.setTransactionSuccessful()
-                    return null
+                    __db.beginTransaction()
+                    try {
+                        _stmt.executeInsert()
+                        __db.setTransactionSuccessful()
+                        return null
+                    } finally {
+                        __db.endTransaction()
+                    }
                 } finally {
-                    __db.endTransaction()
                     __preparedStmtOfInsertPublisherSingle.release(_stmt)
                 }
             }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
index 65888262..8ce5067 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
@@ -58,12 +58,15 @@
         val _stmt: SupportSQLiteStatement = __preparedStmtOfInsertEntity.acquire()
         var _argIndex: Int = 1
         _stmt.bindLong(_argIndex, id)
-        __db.beginTransaction()
         try {
-            _stmt.executeInsert()
-            __db.setTransactionSuccessful()
+            __db.beginTransaction()
+            try {
+                _stmt.executeInsert()
+                __db.setTransactionSuccessful()
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfInsertEntity.release(_stmt)
         }
     }
@@ -73,13 +76,16 @@
         val _stmt: SupportSQLiteStatement = __preparedStmtOfInsertEntity.acquire()
         var _argIndex: Int = 1
         _stmt.bindLong(_argIndex, id)
-        __db.beginTransaction()
         try {
-            val _result: Long = _stmt.executeInsert()
-            __db.setTransactionSuccessful()
-            return _result
+            __db.beginTransaction()
+            try {
+                val _result: Long = _stmt.executeInsert()
+                __db.setTransactionSuccessful()
+                return _result
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfInsertEntity.release(_stmt)
         }
     }
@@ -89,12 +95,15 @@
         val _stmt: SupportSQLiteStatement = __preparedStmtOfUpdateEntity.acquire()
         var _argIndex: Int = 1
         _stmt.bindString(_argIndex, text)
-        __db.beginTransaction()
         try {
-            _stmt.executeUpdateDelete()
-            __db.setTransactionSuccessful()
+            __db.beginTransaction()
+            try {
+                _stmt.executeUpdateDelete()
+                __db.setTransactionSuccessful()
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfUpdateEntity.release(_stmt)
         }
     }
@@ -106,13 +115,16 @@
         _stmt.bindString(_argIndex, text)
         _argIndex = 2
         _stmt.bindLong(_argIndex, id)
-        __db.beginTransaction()
         try {
-            val _result: Int = _stmt.executeUpdateDelete()
-            __db.setTransactionSuccessful()
-            return _result
+            __db.beginTransaction()
+            try {
+                val _result: Int = _stmt.executeUpdateDelete()
+                __db.setTransactionSuccessful()
+                return _result
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfUpdateEntityReturnInt.release(_stmt)
         }
     }
@@ -120,12 +132,15 @@
     public override fun deleteEntity(): Unit {
         __db.assertNotSuspendingTransaction()
         val _stmt: SupportSQLiteStatement = __preparedStmtOfDeleteEntity.acquire()
-        __db.beginTransaction()
         try {
-            _stmt.executeUpdateDelete()
-            __db.setTransactionSuccessful()
+            __db.beginTransaction()
+            try {
+                _stmt.executeUpdateDelete()
+                __db.setTransactionSuccessful()
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfDeleteEntity.release(_stmt)
         }
     }
@@ -133,13 +148,16 @@
     public override fun deleteEntityReturnInt(): Int {
         __db.assertNotSuspendingTransaction()
         val _stmt: SupportSQLiteStatement = __preparedStmtOfDeleteEntity.acquire()
-        __db.beginTransaction()
         try {
-            val _result: Int = _stmt.executeUpdateDelete()
-            __db.setTransactionSuccessful()
-            return _result
+            __db.beginTransaction()
+            try {
+                val _result: Int = _stmt.executeUpdateDelete()
+                __db.setTransactionSuccessful()
+                return _result
+            } finally {
+                __db.endTransaction()
+            }
         } finally {
-            __db.endTransaction()
             __preparedStmtOfDeleteEntity.release(_stmt)
         }
     }
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 aa853d7..91bfcab 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
@@ -19,6 +19,8 @@
 import android.util.Log;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.annotation.NonNull;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -27,8 +29,8 @@
 import java.util.regex.Pattern;
 
 /**
- * A utility class which provides static methods for searching the {@link AccessibilityNodeInfo}
- * hierarchy for nodes that match {@link BySelector} criteria.
+ * Provides utility methods for searching the {@link AccessibilityNodeInfo} hierarchy for nodes
+ * that match {@link BySelector}s.
  */
 class ByMatcher {
 
@@ -39,12 +41,10 @@
     private final boolean mShortCircuit;
 
     /**
-     * Constructs a new {@link ByMatcher} instance. Used by
-     * {@link ByMatcher#findMatch(UiDevice, BySelector, AccessibilityNodeInfo...)} to store state information
-     * that does not change during recursive calls.
+     * Constructs a {@link ByMatcher} instance to store parameters for recursive searches.
      *
-     * @param selector The criteria used to determine if a {@link AccessibilityNodeInfo} is a match.
-     * @param shortCircuit If true, this method will return early when the first match is found.
+     * @param selector     search criteria
+     * @param shortCircuit true to stop searching when the first match is found
      */
     private ByMatcher(UiDevice device, BySelector selector, boolean shortCircuit) {
         mDevice = device;
@@ -53,19 +53,11 @@
     }
 
     /**
-     * Traverses the {@link AccessibilityNodeInfo} hierarchy starting at each {@code root} and
-     * returns the first node to match the {@code selector} criteria. <br />
-     * <strong>Note:</strong> The caller must release the {@link AccessibilityNodeInfo} instance by
-     * calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
-     *
-     * @param device A reference to the {@link UiDevice}.
-     * @param selector The {@link BySelector} criteria used to determine if a node is a match.
-     * @return The first {@link AccessibilityNodeInfo} which matched the search criteria.
+     * Searches the hierarchy under each root for a node that matches the selector.
+     * <p>Note: call {@link AccessibilityNodeInfo#recycle()} when done to avoid leaking resources.
      */
     static AccessibilityNodeInfo findMatch(UiDevice device, BySelector selector,
             AccessibilityNodeInfo... roots) {
-
-        // TODO: Don't short-circuit when debugging, and warn if more than one match.
         ByMatcher matcher = new ByMatcher(device, selector, true);
         for (AccessibilityNodeInfo root : roots) {
             List<AccessibilityNodeInfo> matches = matcher.findMatches(root);
@@ -77,18 +69,11 @@
     }
 
     /**
-     * Traverses the {@link AccessibilityNodeInfo} hierarchy starting at each {@code root} and
-     * returns a list of nodes which match the {@code selector} criteria. <br />
-     * <strong>Note:</strong> The caller must release each {@link AccessibilityNodeInfo} instance
-     * by calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
-     *
-     * @param device A reference to the {@link UiDevice}.
-     * @param selector The {@link BySelector} criteria used to determine if a node is a match.
-     * @return A list containing all of the nodes which matched the search criteria.
+     * Searches the hierarchy under each root for nodes that match the selector.
+     * <p>Note: call {@link AccessibilityNodeInfo#recycle()} when done to avoid leaking resources.
      */
     static List<AccessibilityNodeInfo> findMatches(UiDevice device, BySelector selector,
             AccessibilityNodeInfo... roots) {
-
         List<AccessibilityNodeInfo> ret = new ArrayList<>();
         ByMatcher matcher = new ByMatcher(device, selector, false);
         for (AccessibilityNodeInfo root : roots) {
@@ -97,43 +82,27 @@
         return ret;
     }
 
-    /**
-     * Traverses the {@link AccessibilityNodeInfo} hierarchy starting at {@code root}, and returns
-     * a list of nodes which match the {@code selector} criteria. <br />
-     * <strong>Note:</strong> The caller must release each {@link AccessibilityNodeInfo} instance
-     * by calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
-     *
-     * @param root The root {@link AccessibilityNodeInfo} from which to start the search.
-     * @return A list containing all of the nodes which matched the search criteria.
-     */
+    /** Searches the hierarchy under the root for nodes that match the selector. */
     private List<AccessibilityNodeInfo> findMatches(AccessibilityNodeInfo root) {
-        List<AccessibilityNodeInfo> ret =
-                findMatches(root, 0, 0, new SinglyLinkedList<>());
-
-        // If no matches were found
+        List<AccessibilityNodeInfo> ret = findMatches(root, 0, new PartialMatchList());
         if (ret.isEmpty()) {
-            // Run watchers and retry
+            // No matches found, run watchers and retry.
             mDevice.runWatchers();
-            ret = findMatches(root, 0, 0, new SinglyLinkedList<>());
+            ret = findMatches(root, 0, new PartialMatchList());
         }
-
         return ret;
     }
 
     /**
-     * Traverses the {@link AccessibilityNodeInfo} hierarchy starting at {@code node}, and returns
-     * a list of nodes which match the {@code selector} criteria. <br />
-     * <strong>Note:</strong> The caller must release each {@link AccessibilityNodeInfo} instance
-     * by calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
+     * Recursively searches a node's hierarchy for nodes that match the search criteria.
      *
-     * @param node The root of the {@link AccessibilityNodeInfo} subtree we are currently searching.
-     * @param index The index of this node underneath its parent.
-     * @param depth The distance between {@code node} and the root node.
-     * @param partialMatches The current list of {@link PartialMatch}es that need to be updated.
-     * @return A {@link List} of {@link AccessibilityNodeInfo}s that meet the search criteria.
+     * @param node           node to search
+     * @param depth          distance between the node and the search root
+     * @param partialMatches list of potential matches to track
+     * @return list of matching nodes
      */
-    private List<AccessibilityNodeInfo> findMatches(AccessibilityNodeInfo node,
-            int index, int depth, SinglyLinkedList<PartialMatch> partialMatches) {
+    private List<AccessibilityNodeInfo> findMatches(AccessibilityNodeInfo node, int depth,
+            PartialMatchList partialMatches) {
         List<AccessibilityNodeInfo> ret = new ArrayList<>();
 
         // Don't bother searching the subtree if it is not visible
@@ -144,13 +113,13 @@
 
         // Update partial matches
         for (PartialMatch partialMatch : partialMatches) {
-            partialMatches = partialMatch.update(node, index, depth, partialMatches);
+            partialMatches = partialMatch.matchChildren(node, depth, partialMatches);
         }
 
         // Create a new match, if necessary
-        PartialMatch currentMatch = PartialMatch.accept(node, mSelector, index, depth);
+        PartialMatch currentMatch = PartialMatch.create(mSelector, node, depth, depth);
         if (currentMatch != null) {
-            partialMatches = SinglyLinkedList.prepend(currentMatch, partialMatches);
+            partialMatches = partialMatches.prepend(currentMatch);
         }
 
         // For each child
@@ -168,7 +137,7 @@
             }
 
             // Add any matches found under the child subtree
-            ret.addAll(findMatches(child, i, depth + 1, partialMatches));
+            ret.addAll(findMatches(child, depth + 1, partialMatches));
 
             // We're done with the child
             child.recycle();
@@ -180,180 +149,141 @@
         }
 
         // Finalize match, if necessary
-        if (currentMatch != null && currentMatch.finalizeMatch()) {
+        if (currentMatch != null && currentMatch.isComplete()) {
             ret.add(AccessibilityNodeInfo.obtain(node));
         }
 
         return ret;
     }
 
-    /** Helper method used to evaluate a {@link Pattern} criteria if it is set. */
-    static boolean checkCriteria(Pattern criteria, CharSequence value) {
-        if (criteria == null) {
-            return true;
-        }
-        return criteria.matcher(value != null ? value : "").matches();
-    }
-
-    /** Helper method used to evaluate a {@link Boolean} criteria if it is set. */
-    static boolean checkCriteria(Boolean criteria, boolean value) {
-        if (criteria == null) {
-            return true;
-        }
-        return criteria.equals(value);
-    }
-
     /**
-     * A {@link PartialMatch} instance represents a potential match against the given
-     * {@link BySelector}. Attributes of the current node itself have been evaluated, but any child
-     * selectors have not, since we must first visit the subtree under the node before we can tell
-     * if the child selectors were matched.
+     * Represents a potential match with a {@link BySelector}. The attributes of the selector were
+     * matched, but its child selectors may not have been matched.
      */
-    static private class PartialMatch {
-        private final int matchDepth;
-        private final BySelector matchSelector;
-        private final List<PartialMatch> partialMatches = new ArrayList<>();
+    private static class PartialMatch {
+        private final BySelector mMatchSelector;
+        private final int mMatchDepth;
+        private final List<PartialMatch> mChildMatches = new ArrayList<>();
 
-        /**
-         * Private constructor. Should be instanciated by calling the
-         * {@link PartialMatch#accept(AccessibilityNodeInfo, BySelector, int, int)} factory method.
-         */
         private PartialMatch(BySelector selector, int depth) {
-            matchSelector = selector;
-            matchDepth = depth;
+            mMatchSelector = selector;
+            mMatchDepth = depth;
         }
 
         /**
-         * Factory method which returns a new {@link PartialMatch} if the node partially matches
-         * the {@code selector}, otherwise returns null.
+         * Creates a match if the provided selector matches the node.
          *
-         * @param node The node to check.
-         * @param selector The criteria used to evaluate the node.
-         * @param index The index of this node underneath its parent.
-         * @param depth The distance between {@code node} and the root node.
-         * @return A {@link PartialMatch} instance if the node matches all non-child selector
-         * criteria, otherwise null.
+         * @param selector      search criteria
+         * @param node          node to check
+         * @param absoluteDepth distance between the node and the search root
+         * @param relativeDepth distance between the node and its relevant ancestor
+         * @return potential match or null
          */
-        public static PartialMatch accept(AccessibilityNodeInfo node, BySelector selector,
-                int index, int depth) {
-            return accept(node, selector, index, depth, depth);
-        }
-
-        /**
-         * Factory method which returns a new {@link PartialMatch} if the node partially matches
-         * the {@code selector}, otherwise returns null.
-         *
-         * @param node The node to check.
-         * @param selector The criteria used to evaluate the node.
-         * @param index The index of this node underneath its parent.
-         * @param absoluteDepth The distance between {@code node} and the root node.
-         * @param relativeDepth The distance between {@code node} and the matching ancestor.
-         * @return A {@link PartialMatch} instance if the node matches all non-child selector
-         * criteria, otherwise null.
-         */
-        public static PartialMatch accept(AccessibilityNodeInfo node, BySelector selector,
-                int index, int absoluteDepth, int relativeDepth) {
-
-            if ((selector.mMinDepth != null && relativeDepth < selector.mMinDepth) ||
-                    (selector.mMaxDepth != null && relativeDepth > selector.mMaxDepth)) {
+        static PartialMatch create(BySelector selector, AccessibilityNodeInfo node,
+                int absoluteDepth, int relativeDepth) {
+            if (!matchesSelector(selector, node, relativeDepth)) {
                 return null;
             }
-
-            // NB: index is not checked, as it is not a BySelector criteria (yet). Keeping the
-            // parameter in place in case matching on index is really needed.
-
-            PartialMatch ret = null;
-            if (checkCriteria(selector.mClazz, node.getClassName()) &&
-                    checkCriteria(selector.mDesc, node.getContentDescription()) &&
-                    checkCriteria(selector.mPkg, node.getPackageName()) &&
-                    checkCriteria(selector.mRes, node.getViewIdResourceName()) &&
-                    checkCriteria(selector.mText, node.getText()) &&
-                    checkCriteria(selector.mChecked, node.isChecked()) &&
-                    checkCriteria(selector.mCheckable, node.isCheckable()) &&
-                    checkCriteria(selector.mClickable, node.isClickable()) &&
-                    checkCriteria(selector.mEnabled, node.isEnabled()) &&
-                    checkCriteria(selector.mFocused, node.isFocused()) &&
-                    checkCriteria(selector.mFocusable, node.isFocusable()) &&
-                    checkCriteria(selector.mLongClickable, node.isLongClickable()) &&
-                    checkCriteria(selector.mScrollable, node.isScrollable()) &&
-                    checkCriteria(selector.mSelected, node.isSelected())) {
-
-                ret = new PartialMatch(selector, absoluteDepth);
-            }
-            return ret;
+            return new PartialMatch(selector, absoluteDepth);
         }
 
         /**
-         * Updates this {@link PartialMatch} as part of the tree traversal. Checks to see if
-         * {@code node} matches any of the child selectors that need to match for this
-         * {@link PartialMatch} to be considered a full match.
+         * Returns true if the node matches the selector, ignoring child selectors.
          *
-         * @param node The node to process.
-         * @param index The index of this node underneath its parent.
-         * @param depth The distance between {@code node} and the root node.
-         * @param rest The list of {@link PartialMatch}es that our caller is currently tracking
-         * @return The list of {@link PartialMatch}es that our caller should track while traversing
-         * the subtree under this node.
+         * @param selector search criteria to match
+         * @param node node to check
+         * @param depth distance between the node and its relevant ancestor
          */
-        public SinglyLinkedList<PartialMatch> update(AccessibilityNodeInfo node,
-                int index, int depth, SinglyLinkedList<PartialMatch> rest) {
+        private static boolean matchesSelector(
+                BySelector selector, AccessibilityNodeInfo node, int depth) {
+            return (selector.mMinDepth == null || depth >= selector.mMinDepth)
+                    && (selector.mMaxDepth == null || depth <= selector.mMaxDepth)
+                    && matchesCriteria(selector.mClazz, node.getClassName())
+                    && matchesCriteria(selector.mDesc, node.getContentDescription())
+                    && matchesCriteria(selector.mPkg, node.getPackageName())
+                    && matchesCriteria(selector.mRes, node.getViewIdResourceName())
+                    && matchesCriteria(selector.mText, node.getText())
+                    && matchesCriteria(selector.mChecked, node.isChecked())
+                    && matchesCriteria(selector.mCheckable, node.isCheckable())
+                    && matchesCriteria(selector.mClickable, node.isClickable())
+                    && matchesCriteria(selector.mEnabled, node.isEnabled())
+                    && matchesCriteria(selector.mFocused, node.isFocused())
+                    && matchesCriteria(selector.mFocusable, node.isFocusable())
+                    && matchesCriteria(selector.mLongClickable, node.isLongClickable())
+                    && matchesCriteria(selector.mScrollable, node.isScrollable())
+                    && matchesCriteria(selector.mSelected, node.isSelected());
+        }
 
-            // Check if this node matches any of our children
-            for (BySelector childSelector : matchSelector.mChildSelectors) {
-                PartialMatch m = PartialMatch.accept(node, childSelector, index, depth,
-                        depth - matchDepth);
-                if (m != null) {
-                    partialMatches.add(m);
-                    rest = SinglyLinkedList.prepend(m, rest);
-                }
+        /** Returns true if the criteria is null or matches the value. */
+        private static boolean matchesCriteria(Pattern criteria, CharSequence value) {
+            if (criteria == null) {
+                return true;
             }
-            return rest;
+            return criteria.matcher(value != null ? value : "").matches();
+        }
+
+        /** Returns true if the criteria is null or equal to the value. */
+        private static boolean matchesCriteria(Boolean criteria, boolean value) {
+            return criteria == null || criteria.equals(value);
         }
 
         /**
-         * Finalizes this {@link PartialMatch} and returns true if it was a full match, or false
-         * otherwise.
+         * Checks whether a node matches any child selector. Creates a child match if it does and
+         * adds it to the list of tracked matches.
+         *
+         * @param node           node to check
+         * @param depth          distance between the node and the search root
+         * @param partialMatches list of matches to track
+         * @return new list of matches to track
          */
-        public boolean finalizeMatch() {
-            // Find out which of our child selectors were fully matched
-            Set<BySelector> matches = new HashSet<>();
-            for (PartialMatch p : partialMatches) {
-                if (p.finalizeMatch()) {
-                    matches.add(p.matchSelector);
+        PartialMatchList matchChildren(AccessibilityNodeInfo node, int depth,
+                PartialMatchList partialMatches) {
+            for (BySelector childSelector : mMatchSelector.mChildSelectors) {
+                PartialMatch pm = PartialMatch.create(
+                        childSelector, node, depth, depth - mMatchDepth);
+                if (pm != null) {
+                    mChildMatches.add(pm);
+                    partialMatches = partialMatches.prepend(pm);
                 }
             }
+            return partialMatches;
+        }
 
-            // Return true if matches were found for all of the child selectors
-            return matches.containsAll(matchSelector.mChildSelectors);
+        /** Returns true if all child selectors were matched. */
+        boolean isComplete() {
+            Set<BySelector> matches = new HashSet<>();
+            for (PartialMatch pm : mChildMatches) {
+                if (pm.isComplete()) {
+                    matches.add(pm.mMatchSelector);
+                }
+            }
+            return matches.containsAll(mMatchSelector.mChildSelectors);
         }
     }
 
-    /**
-     * Immutable, singly-linked List. Used to keep track of the {@link PartialMatch}es that we
-     * need to update when visiting nodes.
-     */
-    private static class SinglyLinkedList<T> implements Iterable<T> {
+    /** Immutable singly-linked list of matches that is safe for tree traversal. */
+    private static class PartialMatchList implements Iterable<PartialMatch> {
 
-        final Node<T> mHead;
+        final Node mHead;
 
-        /** Constructs an empty list. */
-        public SinglyLinkedList() {
+        PartialMatchList() {
             this(null);
         }
 
-        private SinglyLinkedList(Node<T> head) {
+        private PartialMatchList(Node head) {
             mHead = head;
         }
 
-        /** Returns a new list obtained by prepending {@code data} to {@code rest}. */
-        public static <T> SinglyLinkedList<T> prepend(T data, SinglyLinkedList<T> rest) {
-            return new SinglyLinkedList<>(new Node<>(data, rest.mHead));
+        /** Returns a new list obtained by prepending a match. */
+        PartialMatchList prepend(PartialMatch match) {
+            return new PartialMatchList(new Node(match, mHead));
         }
 
         @Override
-        public Iterator<T> iterator() {
-            return new Iterator<T>() {
-                private Node<T> mNext = mHead;
+        @NonNull
+        public Iterator<PartialMatch> iterator() {
+            return new Iterator<PartialMatch>() {
+                private Node mNext = mHead;
 
                 @Override
                 public boolean hasNext() {
@@ -361,26 +291,21 @@
                 }
 
                 @Override
-                public T next() {
-                    T ret = mNext.data;
-                    mNext = mNext.next;
-                    return ret;
-                }
-
-                @Override
-                public void remove() {
-                    throw new UnsupportedOperationException();
+                public PartialMatch next() {
+                    PartialMatch match = mNext.mMatch;
+                    mNext = mNext.mNext;
+                    return match;
                 }
             };
         }
 
-        private static class Node<T> {
-            public final T data;
-            public final Node<T> next;
+        private static class Node {
+            final PartialMatch mMatch;
+            final Node mNext;
 
-            public Node(T d, Node<T> n) {
-                data = d;
-                next = n;
+            Node(PartialMatch match, Node next) {
+                mMatch = match;
+                mNext = next;
             }
         }
     }
diff --git a/testutils/testutils-datastore/build.gradle b/testutils/testutils-datastore/build.gradle
index 0ab9be5..3b3e7ef 100644
--- a/testutils/testutils-datastore/build.gradle
+++ b/testutils/testutils-datastore/build.gradle
@@ -19,6 +19,7 @@
 plugins {
     id("AndroidXPlugin")
 }
+
 androidXMultiplatform {
     jvm {}
     mac()
@@ -33,6 +34,7 @@
                 api(libs.kotlinStdlibCommon)
                 api(libs.kotlinTestCommon)
                 api(libs.kotlinCoroutinesCore)
+                api(libs.kotlinCoroutinesTest)
                 api(libs.okio)
             }
         }
diff --git a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
index 40c640c..733d614 100644
--- a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
@@ -16,6 +16,7 @@
 
 package androidx.datastore
 
+import androidx.datastore.core.InterProcessCoordinator
 import androidx.datastore.core.Storage
 import androidx.datastore.core.okio.OkioStorage
 import kotlin.random.Random
@@ -25,29 +26,54 @@
 import okio.IOException
 import okio.Path.Companion.toPath
 
-class OkioTestIO(dirName: String = "test-dir") : TestIO<OkioPath, IOException>(dirName) {
+open class OkioTestIO(dirName: String = "test-dir") : TestIO<OkioPath, IOException>(dirName) {
     private val fileSystem: FileSystem = FileSystem.SYSTEM
     override fun getStorage(
         serializerConfig: TestingSerializerConfig,
+        coordinatorProducer: () -> InterProcessCoordinator,
         futureFile: () -> TestFile
     ): Storage<Byte> {
-        return OkioStorage(fileSystem, TestingOkioSerializer(serializerConfig)) {
+        return OkioStorage(
+            fileSystem = fileSystem,
+            serializer = TestingOkioSerializer(serializerConfig),
+            coordinatorProducer = { _, _ -> coordinatorProducer() }
+        ) {
             futureFile().getAbsolutePath().toPath()
         }
     }
 
-    override fun tempDir(directoryPath: String?, makeDirs: Boolean): OkioPath {
-        return if (directoryPath != null) {
-            val newPath = if (directoryPath.startsWith("/"))
-                directoryPath.substring(1) else directoryPath
-            val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY / randomFileName(dirName) / newPath
-            if (makeDirs) {
-                fileSystem.createDirectories(dir)
+    override fun tempDir(
+        directoryPath: String?,
+        makeDirs: Boolean,
+        parentDir: OkioPath?
+    ): OkioPath {
+        return if (parentDir != null) {
+            if (directoryPath != null) {
+                val newPath = if (directoryPath.startsWith("/"))
+                    directoryPath.substring(1) else directoryPath
+                val dir = parentDir.path / newPath
+                if (makeDirs) {
+                    fileSystem.createDirectories(dir)
+                }
+                OkioPath(fileSystem, dir)
+            } else {
+                OkioPath(fileSystem, parentDir.path / randomFileName(dirName))
             }
-            OkioPath(fileSystem, dir)
         } else {
-            OkioPath(fileSystem, FileSystem.SYSTEM_TEMPORARY_DIRECTORY /
-                randomFileName(dirName))
+            if (directoryPath != null) {
+                val newPath = if (directoryPath.startsWith("/"))
+                    directoryPath.substring(1) else directoryPath
+                val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY / randomFileName(dirName) / newPath
+                if (makeDirs) {
+                    fileSystem.createDirectories(dir)
+                }
+                OkioPath(fileSystem, dir)
+            } else {
+                OkioPath(
+                    fileSystem, FileSystem.SYSTEM_TEMPORARY_DIRECTORY /
+                        randomFileName(dirName)
+                )
+            }
         }
     }
 
@@ -93,4 +119,11 @@
     override fun exists(): Boolean {
         return fileSystem.exists(path)
     }
+
+    override fun createIfNotExists(): Boolean {
+        if (exists()) return false
+        path.parent?.let { fileSystem.createDirectories(it) }
+        fileSystem.write(path, mustCreate = true) { "" }
+        return true
+    }
 }
\ No newline at end of file
diff --git a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
index 03bad11..bddb036 100644
--- a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
@@ -19,9 +19,12 @@
 import androidx.datastore.core.DataStore
 import androidx.datastore.core.Storage
 import androidx.datastore.core.DataStoreFactory.create
+import androidx.datastore.core.InterProcessCoordinator
 import kotlin.reflect.KClass
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+@OptIn(ExperimentalCoroutinesApi::class)
 abstract class TestIO<F : TestFile, IOE : Throwable>(
     protected val dirName: String = "datastore-test-dir"
 ) {
@@ -29,17 +32,24 @@
     fun getStore(
         serializerConfig: TestingSerializerConfig,
         scope: CoroutineScope,
+        coordinatorProducer: () -> InterProcessCoordinator,
         futureFile: () -> TestFile
     ): DataStore<Byte> {
-        return create(getStorage(serializerConfig, futureFile), scope = scope)
+        return create(getStorage(serializerConfig, coordinatorProducer, futureFile), scope = scope)
     }
 
     abstract fun getStorage(
         serializerConfig: TestingSerializerConfig,
+        coordinatorProducer: () -> InterProcessCoordinator,
         futureFile: () -> TestFile = { newTempFile() }
     ): Storage<Byte>
 
-    abstract fun tempDir(directoryPath: String? = null, makeDirs: Boolean = true): F
+    abstract fun tempDir(
+        directoryPath: String? = null,
+        makeDirs: Boolean = true,
+        parentDir: F? = null
+    ): F
+
     abstract fun newTempFile(tempFolder: F = tempDir()): F
 
     abstract fun ioException(message: String): IOE
@@ -62,6 +72,12 @@
      */
     abstract fun exists(): Boolean
 
+    /**
+     * Creates the file if it doesn't exist.
+     * @return `true` if file didn't exist and gets created and false otherwise.
+     */
+    abstract fun createIfNotExists(): Boolean
+
     fun deleteIfExists() {
         if (exists()) {
             delete()
diff --git a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
index cdfff76..a9565b9 100644
--- a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
@@ -25,7 +25,7 @@
 import okio.use
 
 class TestingOkioSerializer(
-    val config: TestingSerializerConfig
+    private val config: TestingSerializerConfig
 ) : OkioSerializer<Byte> {
 
     override suspend fun readFrom(source: BufferedSource): Byte {
diff --git a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
index 839e7ce..8345eee 100644
--- a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
+++ b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
@@ -17,6 +17,7 @@
 package androidx.datastore
 
 import androidx.datastore.core.FileStorage
+import androidx.datastore.core.InterProcessCoordinator
 import androidx.datastore.core.Storage
 import androidx.datastore.core.TestingSerializer
 import java.io.File
@@ -26,16 +27,36 @@
 
 class FileTestIO(dirName: String = "test-dir") : TestIO<JavaIOFile, IOException>(dirName) {
 
-    override fun tempDir(directoryPath: String?, makeDirs: Boolean): JavaIOFile {
-        return if (directoryPath != null) {
-            val tempRoot = File.createTempFile("placeholder", "placeholder").parentFile
-            val tempPath = File(tempRoot, directoryPath)
-            if (makeDirs) {
-                tempPath.mkdirs()
+    override fun tempDir(
+        directoryPath: String?,
+        makeDirs: Boolean,
+        parentDir: JavaIOFile?
+    ): JavaIOFile {
+        return if (parentDir != null) {
+            if (directoryPath != null) {
+                val tempPath = File(parentDir.file, directoryPath)
+                if (makeDirs) {
+                    tempPath.mkdirs()
+                }
+                tempPath.toJavaFile()
+            } else {
+                val tempPath = File(parentDir.file, "tempPath")
+                if (makeDirs) {
+                    tempPath.mkdirs()
+                }
+                tempPath.toJavaFile()
             }
-            tempPath.toJavaFile()
         } else {
-            File.createTempFile("temp", "tmp").parentFile.toJavaFile()
+            if (directoryPath != null) {
+                val tempRoot = File.createTempFile("placeholder", "placeholder").parentFile
+                val tempPath = File(tempRoot, directoryPath)
+                if (makeDirs) {
+                    tempPath.mkdirs()
+                }
+                tempPath.toJavaFile()
+            } else {
+                File.createTempFile("temp", "tmp").parentFile!!.toJavaFile()
+            }
         }
     }
 
@@ -44,6 +65,9 @@
          * We need to create a new temp file without creating it so we use a UUID to decide the
          * file name but also try 100 times in case a collision happens.
          */
+        // TODO use a consistent naming scheme (e.g. incrementing counter) when b/276983736 is
+        //  fixed. We'll need these TestIO classes to do proper cleanup after themselves to be able
+        //  do that.
         repeat(100) {
             val randName = UUID.randomUUID().toString().take(10)
             val tmpFile = File(tempFolder.file, "temp-$randName-temp")
@@ -56,9 +80,10 @@
 
     override fun getStorage(
         serializerConfig: TestingSerializerConfig,
+        coordinatorProducer: () -> InterProcessCoordinator,
         futureFile: () -> TestFile
     ): Storage<Byte> {
-        return FileStorage(TestingSerializer(serializerConfig)) {
+        return FileStorage(TestingSerializer(serializerConfig), { coordinatorProducer() }) {
             (futureFile() as JavaIOFile).file
         }
     }
@@ -87,6 +112,12 @@
     override fun exists(): Boolean {
         return file.exists()
     }
+
+    override fun createIfNotExists(): Boolean {
+        if (exists()) return false
+        file.createNewFile()
+        return true
+    }
 }
 
 fun File.toJavaFile(): JavaIOFile {
diff --git a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
index 3027ddd..ab60f1f 100644
--- a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
+++ b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
@@ -22,7 +22,7 @@
 import java.io.OutputStream
 
 class TestingSerializer(
-    val config: TestingSerializerConfig = TestingSerializerConfig(),
+    private val config: TestingSerializerConfig = TestingSerializerConfig(),
 ) : Serializer<Byte> {
     override suspend fun readFrom(input: InputStream): Byte {
         // hack to make failReadWithCorruptionException during runtime
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index 72025d1..44ffb2b 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -70,10 +70,16 @@
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardDefaults {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.CardBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.CardColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.CardColors compactCardColors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor);
+    method public androidx.compose.ui.graphics.Brush getContainerGradient();
     method public androidx.tv.material3.CardGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
     method public androidx.tv.material3.CardScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale);
     method public androidx.tv.material3.CardShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape);
+    property public final androidx.compose.ui.graphics.Brush ContainerGradient;
+    field public static final float HorizontalImageAspectRatio = 1.7777778f;
     field public static final androidx.tv.material3.CardDefaults INSTANCE;
+    field public static final float SquareImageAspectRatio = 1.0f;
+    field public static final float VerticalImageAspectRatio = 0.6666667f;
   }
 
   @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardGlow {
@@ -81,6 +87,9 @@
 
   public final class CardKt {
     method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CardShape shape, optional androidx.tv.material3.CardColors colors, optional androidx.tv.material3.CardScale scale, optional androidx.tv.material3.CardBorder border, optional androidx.tv.material3.CardGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ClassicCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> image, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> subtitle, optional kotlin.jvm.functions.Function0<kotlin.Unit> description, optional androidx.tv.material3.CardShape shape, optional androidx.tv.material3.CardColors colors, optional androidx.tv.material3.CardScale scale, optional androidx.tv.material3.CardBorder border, optional androidx.tv.material3.CardGlow glow, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void CompactCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> image, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> subtitle, optional kotlin.jvm.functions.Function0<kotlin.Unit> description, optional androidx.tv.material3.CardShape shape, optional androidx.tv.material3.CardColors colors, optional androidx.tv.material3.CardScale scale, optional androidx.tv.material3.CardBorder border, optional androidx.tv.material3.CardGlow glow, optional androidx.compose.ui.graphics.Brush scrimBrush, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void WideClassicCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> image, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> subtitle, optional kotlin.jvm.functions.Function0<kotlin.Unit> description, optional androidx.tv.material3.CardShape shape, optional androidx.tv.material3.CardColors colors, optional androidx.tv.material3.CardScale scale, optional androidx.tv.material3.CardBorder border, optional androidx.tv.material3.CardGlow glow, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
   @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardScale {
@@ -529,5 +538,23 @@
     property public final androidx.compose.ui.text.TextStyle titleSmall;
   }
 
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class WideButtonContentColor {
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class WideButtonDefaults {
+    method @androidx.compose.runtime.Composable public void Background(boolean enabled, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ButtonBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedDisabledBorder);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.WideButtonContentColor contentColor(optional long color, optional long focusedColor, optional long pressedColor, optional long disabledColor);
+    method public androidx.tv.material3.ButtonGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
+    method public androidx.tv.material3.ButtonScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedDisabledScale);
+    method public androidx.tv.material3.ButtonShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape);
+    field public static final androidx.tv.material3.WideButtonDefaults INSTANCE;
+  }
+
+  public final class WideButtonKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void WideButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.tv.material3.ButtonScale scale, optional androidx.tv.material3.ButtonGlow glow, optional androidx.tv.material3.ButtonShape shape, optional androidx.tv.material3.WideButtonContentColor contentColor, optional float tonalElevation, optional androidx.tv.material3.ButtonBorder border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void WideButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? subtitle, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.tv.material3.ButtonScale scale, optional androidx.tv.material3.ButtonGlow glow, optional androidx.tv.material3.ButtonShape shape, optional androidx.tv.material3.WideButtonContentColor contentColor, optional float tonalElevation, optional androidx.tv.material3.ButtonBorder border, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+  }
+
 }
 
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardScreenshotTest.kt
new file mode 100644
index 0000000..f613158
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardScreenshotTest.kt
@@ -0,0 +1,393 @@
+/*
+ * 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.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment.Companion.Center
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChild
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+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
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class CardScreenshotTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    private val boxSizeModifier = Modifier.size(220.dp, 180.dp)
+    private val verticalCardSizeModifier = Modifier.size(150.dp, 120.dp)
+    private val horizontalCardSizeModifier = Modifier.size(180.dp, 100.dp)
+
+    @Test
+    fun card_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    Card(
+                        modifier = verticalCardSizeModifier,
+                        onClick = { }
+                    ) {
+                        Box(Modifier.fillMaxSize()) {
+                            Text("Card", Modifier.align(Center))
+                        }
+                    }
+                }
+            }
+        }
+
+        assertAgainstGolden("card_lightTheme")
+    }
+
+    @Test
+    fun card_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    Card(
+                        modifier = verticalCardSizeModifier,
+                        onClick = { }
+                    ) {
+                        Box(Modifier.fillMaxSize()) {
+                            Text("Card", Modifier.align(Center))
+                        }
+                    }
+                }
+            }
+        }
+
+        assertAgainstGolden("card_darkTheme")
+    }
+
+    @Test
+    fun card_focused() {
+        rule.setContent {
+            Box(
+                modifier = boxSizeModifier.testTag(CardWrapperTag),
+                contentAlignment = Center
+            ) {
+                Card(
+                    modifier = verticalCardSizeModifier,
+                    onClick = { }
+                ) {
+                    Box(Modifier.fillMaxSize()) {
+                        Text("Card", Modifier.align(Center))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(CardWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("card_focused")
+    }
+
+    @Test
+    fun classicCard_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    ClassicCard(
+                        modifier = verticalCardSizeModifier,
+                        image = {
+                            SampleImage(
+                                Modifier
+                                    .fillMaxWidth()
+                                    .height(80.dp)
+                            )
+                        },
+                        title = { Text("Classic Card") },
+                        contentPadding = PaddingValues(8.dp),
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("classicCard_lightTheme")
+    }
+
+    @Test
+    fun classicCard_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    ClassicCard(
+                        modifier = verticalCardSizeModifier,
+                        image = {
+                            SampleImage(
+                                Modifier
+                                    .fillMaxWidth()
+                                    .height(80.dp)
+                            )
+                        },
+                        title = { Text("Classic Card") },
+                        contentPadding = PaddingValues(8.dp),
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("classicCard_darkTheme")
+    }
+
+    @Test
+    fun classicCard_focused() {
+        rule.setContent {
+            Box(
+                modifier = boxSizeModifier.testTag(CardWrapperTag),
+                contentAlignment = Center
+            ) {
+                ClassicCard(
+                    modifier = verticalCardSizeModifier,
+                    image = {
+                        SampleImage(
+                            Modifier
+                                .fillMaxWidth()
+                                .height(80.dp)
+                        )
+                    },
+                    title = { Text("Classic Card") },
+                    contentPadding = PaddingValues(8.dp),
+                    onClick = { }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(CardWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("classicCard_focused")
+    }
+
+    @Test
+    fun compactCard_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    CompactCard(
+                        modifier = verticalCardSizeModifier,
+                        image = { SampleImage(Modifier.fillMaxSize()) },
+                        title = { Text("Compact Card", Modifier.padding(8.dp)) },
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("compactCard_lightTheme")
+    }
+
+    @Test
+    fun compactCard_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    CompactCard(
+                        modifier = verticalCardSizeModifier,
+                        image = { SampleImage(Modifier.fillMaxSize()) },
+                        title = { Text("Compact Card", Modifier.padding(8.dp)) },
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("compactCard_darkTheme")
+    }
+
+    @Test
+    fun compactCard_focused() {
+        rule.setContent {
+            Box(
+                modifier = boxSizeModifier.testTag(CardWrapperTag),
+                contentAlignment = Center
+            ) {
+                CompactCard(
+                    modifier = verticalCardSizeModifier,
+                    image = { SampleImage(Modifier.fillMaxSize()) },
+                    title = { Text("Compact Card", Modifier.padding(8.dp)) },
+                    onClick = { }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(CardWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("compactCard_focused")
+    }
+
+    @Test
+    fun wideClassicCard_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    WideClassicCard(
+                        modifier = horizontalCardSizeModifier,
+                        image = {
+                            SampleImage(
+                                Modifier
+                                    .fillMaxHeight()
+                                    .width(80.dp)
+                            )
+                        },
+                        title = { Text("Wide Classic Card", Modifier.padding(start = 8.dp)) },
+                        contentPadding = PaddingValues(8.dp),
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("wideClassicCard_lightTheme")
+    }
+
+    @Test
+    fun wideClassicCard_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Box(
+                    modifier = boxSizeModifier.testTag(CardWrapperTag),
+                    contentAlignment = Center
+                ) {
+                    WideClassicCard(
+                        modifier = horizontalCardSizeModifier,
+                        image = {
+                            SampleImage(
+                                Modifier
+                                    .fillMaxHeight()
+                                    .width(80.dp)
+                            )
+                        },
+                        title = { Text("Wide Classic Card", Modifier.padding(start = 8.dp)) },
+                        contentPadding = PaddingValues(8.dp),
+                        onClick = { }
+                    )
+                }
+            }
+        }
+
+        assertAgainstGolden("wideClassicCard_darkTheme")
+    }
+
+    @Test
+    fun wideClassicCard_focused() {
+        rule.setContent {
+            Box(
+                modifier = boxSizeModifier.testTag(CardWrapperTag),
+                contentAlignment = Center
+            ) {
+                WideClassicCard(
+                    modifier = horizontalCardSizeModifier,
+                    image = {
+                        SampleImage(
+                            Modifier
+                                .fillMaxHeight()
+                                .width(80.dp)
+                        )
+                    },
+                    title = { Text("Wide Classic Card", Modifier.padding(start = 8.dp)) },
+                    contentPadding = PaddingValues(8.dp),
+                    onClick = { }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(CardWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("wideClassicCard_focused")
+    }
+
+    @Composable
+    fun SampleImage(modifier: Modifier = Modifier) {
+        Box(
+            modifier = modifier
+                .background(Color.Blue)
+        )
+    }
+
+    private fun assertAgainstGolden(goldenName: String) {
+        rule.onNodeWithTag(CardWrapperTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenName)
+    }
+}
+
+private const val CardWrapperTag = "card_wrapper"
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt
index d4437c7..3ea23a8 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt
@@ -23,11 +23,14 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.testutils.assertIsEqualTo
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
@@ -44,6 +47,7 @@
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performKeyInput
@@ -51,7 +55,7 @@
 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 androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth
 import kotlinx.coroutines.CoroutineScope
@@ -65,7 +69,7 @@
     ExperimentalComposeUiApi::class,
     ExperimentalTvMaterial3Api::class
 )
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4::class)
 class CardTest {
     @get:Rule
@@ -82,7 +86,7 @@
                 Card(
                     modifier = Modifier
                         .semantics(mergeDescendants = true) {}
-                        .testTag("card"),
+                        .testTag(CardTag),
                     shape = CardDefaults.shape(shape = shape),
                     colors = CardDefaults.colors(containerColor = cardColor),
                     onClick = {}
@@ -93,7 +97,7 @@
         }
 
         rule
-            .onNodeWithTag("card")
+            .onNodeWithTag(CardTag)
             .captureToImage()
             .assertShape(
                 density = rule.density,
@@ -109,14 +113,15 @@
         val count = mutableStateOf(0)
         rule.setContent {
             Card(
-                modifier = Modifier.testTag("card"),
+                modifier = Modifier.testTag(CardTag),
                 onClick = { count.value += 1 },
             ) {
                 Text("${count.value}")
                 Spacer(Modifier.size(30.dp))
             }
         }
-        rule.onNodeWithTag("card")
+
+        rule.onNodeWithTag(CardTag)
             .assertHasClickAction()
             .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Role))
             .performSemanticsAction(SemanticsActions.RequestFocus)
@@ -131,7 +136,7 @@
         val count = mutableStateOf(0f)
         rule.setContent {
             Card(
-                modifier = Modifier.testTag("card"),
+                modifier = Modifier.testTag(CardTag),
                 onClick = { count.value += 1 },
             ) {
                 Text("${count.value}")
@@ -139,12 +144,12 @@
             }
         }
 
-        rule.onNodeWithTag("card")
+        rule.onNodeWithTag(CardTag)
             .performSemanticsAction(SemanticsActions.RequestFocus)
             .performKeyInput { pressKey(Key.DirectionCenter) }
         Truth.assertThat(count.value).isEqualTo(1)
 
-        rule.onNodeWithTag("card")
+        rule.onNodeWithTag(CardTag)
             .performSemanticsAction(SemanticsActions.RequestFocus)
             .performKeyInput { pressKey(Key.DirectionCenter) }
             .performKeyInput { pressKey(Key.DirectionCenter) }
@@ -161,7 +166,7 @@
             scope = rememberCoroutineScope()
             Card(
                 onClick = {},
-                modifier = Modifier.testTag("card"),
+                modifier = Modifier.testTag(CardTag),
                 interactionSource = interactionSource
             ) {
                 Spacer(Modifier.size(30.dp))
@@ -174,14 +179,14 @@
 
         rule.runOnIdle { Truth.assertThat(interactions).isEmpty() }
 
-        rule.onNodeWithTag("card").performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.onNodeWithTag(CardTag).performSemanticsAction(SemanticsActions.RequestFocus)
 
         rule.runOnIdle {
             Truth.assertThat(interactions).hasSize(1)
             Truth.assertThat(interactions.first()).isInstanceOf(FocusInteraction.Focus::class.java)
         }
 
-        rule.onNodeWithTag("card").performKeyInput { pressKey(Key.DirectionCenter) }
+        rule.onNodeWithTag(CardTag).performKeyInput { pressKey(Key.DirectionCenter) }
 
         rule.runOnIdle {
             Truth.assertThat(interactions).hasSize(3)
@@ -190,4 +195,286 @@
             Truth.assertThat(interactions[2]).isInstanceOf(PressInteraction.Release::class.java)
         }
     }
-}
\ No newline at end of file
+
+    @Test
+    fun classicCard_semantics() {
+        val count = mutableStateOf(0)
+        rule.setContent {
+            ClassicCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(ClassicCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(ClassicCardTag)
+            .assertHasClickAction()
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Role))
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .assertIsEnabled()
+            .assertTextEquals("0")
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .assertTextEquals("1")
+    }
+
+    @Test
+    fun classicCard_clickAction() {
+        val count = mutableStateOf(0f)
+        rule.setContent {
+            ClassicCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(ClassicCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(ClassicCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(1)
+
+        rule.onNodeWithTag(ClassicCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(3)
+    }
+
+    @Test
+    fun classicCard_contentPadding() {
+        val contentPadding = PaddingValues(8.dp, 10.dp, 12.dp, 14.dp)
+        val cardTitleTag = "classic_card_title"
+
+        rule.setContent {
+            ClassicCard(
+                modifier = Modifier.testTag(ClassicCardTag),
+                image = { SampleImage() },
+                title = {
+                    Text(
+                        text = "Classic Card",
+                        modifier = Modifier.testTag(cardTitleTag)
+                    )
+                },
+                onClick = {},
+                contentPadding = contentPadding
+            )
+        }
+
+        val cardBounds = rule
+            .onNodeWithTag(ClassicCardTag)
+            .getUnclippedBoundsInRoot()
+
+        val imageBounds = rule
+            .onNodeWithTag(SampleImageTag, true)
+            .getUnclippedBoundsInRoot()
+
+        val titleBounds = rule
+            .onNodeWithTag(cardTitleTag, true)
+            .getUnclippedBoundsInRoot()
+
+        // Check top padding
+        (imageBounds.top - cardBounds.top).assertIsEqualTo(
+            10.dp,
+            "padding between top of the image and top of the card."
+        )
+
+        // Check bottom padding
+        (cardBounds.bottom - titleBounds.bottom).assertIsEqualTo(
+            14.dp,
+            "padding between bottom of the text and bottom of the card."
+        )
+
+        // Check start padding
+        (imageBounds.left - cardBounds.left).assertIsEqualTo(
+            8.dp,
+            "padding between left of the image and left of the card."
+        )
+
+        // Check end padding
+        (cardBounds.right - imageBounds.right).assertIsEqualTo(
+            12.dp,
+            "padding between right of the text and right of the card."
+        )
+    }
+
+    @Test
+    fun compactCard_semantics() {
+        val count = mutableStateOf(0)
+        rule.setContent {
+            CompactCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(CompactCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(CompactCardTag)
+            .assertHasClickAction()
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Role))
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .assertIsEnabled()
+            .assertTextEquals("0")
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .assertTextEquals("1")
+    }
+
+    @Test
+    fun compactCard_clickAction() {
+        val count = mutableStateOf(0f)
+        rule.setContent {
+            CompactCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(CompactCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(CompactCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(1)
+
+        rule.onNodeWithTag(CompactCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(3)
+    }
+
+    @Test
+    fun wideClassicCard_semantics() {
+        val count = mutableStateOf(0)
+        rule.setContent {
+            WideClassicCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(WideClassicCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(WideClassicCardTag)
+            .assertHasClickAction()
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Role))
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .assertIsEnabled()
+            .assertTextEquals("0")
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .assertTextEquals("1")
+    }
+
+    @Test
+    fun wideClassicCard_clickAction() {
+        val count = mutableStateOf(0f)
+        rule.setContent {
+            WideClassicCard(
+                modifier = Modifier
+                    .semantics(mergeDescendants = true) {}
+                    .testTag(WideClassicCardTag),
+                image = { SampleImage() },
+                title = { Text("${count.value}") },
+                onClick = { count.value += 1 }
+            )
+        }
+
+        rule.onNodeWithTag(WideClassicCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(1)
+
+        rule.onNodeWithTag(WideClassicCardTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(3)
+    }
+
+    @Test
+    fun wideClassicCard_contentPadding() {
+        val contentPadding = PaddingValues(8.dp, 10.dp, 12.dp, 14.dp)
+        val cardTitleTag = "wide_classic_card_title"
+
+        rule.setContent {
+            WideClassicCard(
+                modifier = Modifier.testTag(WideClassicCardTag),
+                image = { SampleImage() },
+                title = {
+                    Text(
+                        text = "Wide Classic Card",
+                        modifier = Modifier.testTag(cardTitleTag)
+                    )
+                },
+                onClick = {},
+                contentPadding = contentPadding
+            )
+        }
+
+        val cardBounds = rule
+            .onNodeWithTag(WideClassicCardTag)
+            .getUnclippedBoundsInRoot()
+
+        val imageBounds = rule
+            .onNodeWithTag(SampleImageTag, true)
+            .getUnclippedBoundsInRoot()
+
+        val titleBounds = rule
+            .onNodeWithTag(cardTitleTag, true)
+            .getUnclippedBoundsInRoot()
+
+        // Check top padding
+        (imageBounds.top - cardBounds.top).assertIsEqualTo(
+            10.dp,
+            "padding between top of the image and top of the card."
+        )
+
+        // Check bottom padding
+        (cardBounds.bottom - imageBounds.bottom).assertIsEqualTo(
+            14.dp,
+            "padding between bottom of the text and bottom of the card."
+        )
+
+        // Check start padding
+        (imageBounds.left - cardBounds.left).assertIsEqualTo(
+            8.dp,
+            "padding between left of the image and left of the card."
+        )
+
+        // Check end padding
+        (cardBounds.right - titleBounds.right).assertIsEqualTo(
+            12.dp,
+            "padding between right of the text and right of the card."
+        )
+    }
+
+    @Composable
+    fun SampleImage() {
+        Box(
+            Modifier
+                .size(180.dp, 150.dp)
+                .testTag(SampleImageTag)
+        )
+    }
+}
+
+private const val CardTag = "card"
+private const val StandardCardTag = "standard-card"
+private const val CompactCardTag = "compact-card"
+private const val ClassicCardTag = "classic-card"
+private const val WideCardTag = "wide-card"
+private const val WideClassicCardTag = "wide-classic-card"
+
+private const val SampleImageTag = "sample-image"
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/IconButtonScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/IconButtonScreenshotTest.kt
index 9e6d21a..9cf007a 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/IconButtonScreenshotTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/IconButtonScreenshotTest.kt
@@ -216,7 +216,7 @@
         rule.setContent {
             LightMaterialTheme {
                 localInputModeManager = LocalInputModeManager.current
-                Box(Modifier.sizeIn(minWidth = 50.dp, minHeight = 50.dp).testTag(wrapperTestTag)) {
+                Box(Modifier.sizeIn(minWidth = 52.dp, minHeight = 52.dp).testTag(wrapperTestTag)) {
                     IconButton(
                         onClick = { /* doSomething() */ },
                         modifier = Modifier
@@ -246,7 +246,7 @@
         rule.setContent {
             DarkMaterialTheme {
                 localInputModeManager = LocalInputModeManager.current
-                Box(Modifier.sizeIn(minWidth = 50.dp, minHeight = 50.dp).testTag(wrapperTestTag)) {
+                Box(Modifier.sizeIn(minWidth = 52.dp, minHeight = 52.dp).testTag(wrapperTestTag)) {
                     IconButton(
                         onClick = { /* doSomething() */ },
                         modifier = Modifier
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonScreenshotTest.kt
new file mode 100644
index 0000000..1ee53ca
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonScreenshotTest.kt
@@ -0,0 +1,365 @@
+/*
+ * 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.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.hasClickAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+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
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class WideButtonScreenshotTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    @Test
+    fun defaultWideButton_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(onClick = { }) {
+                    Text("Settings")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_light_theme")
+    }
+
+    @Test
+    fun defaultWideButton_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(onClick = { }) {
+                    Text("Settings")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_dark_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(onClick = { }, enabled = false) {
+                    Text("Settings")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_disabled_light_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(onClick = { }, enabled = false) {
+                    Text("Settings")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_disabled_dark_theme")
+    }
+
+    @Test
+    fun wideButton_withSubtitle_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_subtitle_light_theme")
+    }
+
+    @Test
+    fun wideButton_withSubtitle_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_subtitle_dark_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_withSubtitle_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "disabled_wide_button_with_subtitle_light_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_withSubtitle_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "disabled_wide_button_with_subtitle_dark_theme")
+    }
+
+    @Test
+    fun wideButton_withIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_icon_light_theme")
+    }
+
+    @Test
+    fun wideButton_withIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_icon_dark_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_withIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "disabled_wide_button_with_icon_light_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_withIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "disabled_wide_button_with_icon_dark_theme")
+    }
+
+    @Test
+    fun wideButton_withSubtitleAndIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_subtitle_and_icon_light_theme")
+    }
+
+    @Test
+    fun wideButton_withSubtitleAndIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "wide_button_with_subtitle_and_icon_dark_theme")
+    }
+
+    @Test
+    fun disabled_wideButton_withSubtitleAndIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(
+                screenshotRule,
+                "disabled_wide_button_with_subtitle_and_icon_light_theme"
+            )
+    }
+
+    @Test
+    fun disabled_wideButton_withSubtitleAndIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                WideButton(
+                    onClick = { },
+                    enabled = false,
+                    title = { Text("Settings") },
+                    subtitle = { Text(text = "Update device preferences") },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "Settings"
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(
+                screenshotRule,
+                "disabled_wide_button_with_subtitle_and_icon_dark_theme"
+            )
+    }
+}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonTest.kt
new file mode 100644
index 0000000..d7d9625
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/WideButtonTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.semantics.SemanticsProperties
+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.assertIsEnabled
+import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performKeyInput
+import androidx.compose.ui.test.performSemanticsAction
+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 com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(
+    ExperimentalComposeUiApi::class,
+    ExperimentalTestApi::class,
+    ExperimentalTvMaterial3Api::class
+)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class WideButtonTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun wideButton_defaultSemantics() {
+        rule.setContent {
+            Box {
+                WideButton(
+                    onClick = { },
+                    modifier = Modifier
+                        .testTag(WideButtonTag),
+                    title = { Text(text = "Settings") },
+                    icon = {
+                        Icon(imageVector = Icons.Default.Settings, contentDescription = "")
+                    },
+                    subtitle = { Text(text = "Update device preferences") }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(WideButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun wideButton_disabledSemantics() {
+        rule.setContent {
+            Box {
+                WideButton(
+                    onClick = { },
+                    modifier = Modifier
+                        .testTag(WideButtonTag),
+                    enabled = false,
+                    title = { Text(text = "Settings") },
+                    icon = {
+                        Icon(imageVector = Icons.Default.Settings, contentDescription = "")
+                    },
+                    subtitle = { Text(text = "Update device preferences") }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(WideButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun wideButton_findByTagAndClick() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+
+        rule.setContent {
+            Box {
+                WideButton(
+                    onClick = onClick,
+                    modifier = Modifier
+                        .testTag(WideButtonTag),
+                    title = { Text(text = "Settings") },
+                    icon = {
+                        Icon(imageVector = Icons.Default.Settings, contentDescription = "")
+                    },
+                    subtitle = { Text(text = "Update device preferences") }
+                )
+            }
+        }
+        rule.onNodeWithTag(WideButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        rule.runOnIdle {
+            Truth.assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun wideButton_canBeDisabled() {
+        rule.setContent {
+            var enabled by remember { mutableStateOf(true) }
+            Box {
+                WideButton(
+                    onClick = { enabled = false },
+                    modifier = Modifier
+                        .testTag(WideButtonTag),
+                    enabled = enabled,
+                    title = { Text(text = "Settings") },
+                    icon = {
+                        Icon(imageVector = Icons.Default.Settings, contentDescription = "")
+                    },
+                    subtitle = { Text(text = "Update device preferences") }
+                )
+            }
+        }
+        rule.onNodeWithTag(WideButtonTag)
+            // Confirm the button starts off enabled, with a click action
+            .assertHasClickAction()
+            .assertIsEnabled()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            // Then confirm it's disabled with click action after clicking it
+            .assertHasClickAction()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun wideButton_clickIsIndependentBetweenButtons() {
+        var watchButtonCounter = 0
+        val watchButtonOnClick: () -> Unit = { ++watchButtonCounter }
+        val watchButtonTag = "WatchButton"
+
+        var playButtonCounter = 0
+        val playButtonOnClick: () -> Unit = { ++playButtonCounter }
+        val playButtonTag = "PlayButton"
+
+        rule.setContent {
+            Column {
+                WideButton(
+                    onClick = watchButtonOnClick,
+                    modifier = Modifier.testTag(watchButtonTag),
+                ) {
+                    Text(text = "Watch")
+                }
+                WideButton(
+                    onClick = playButtonOnClick,
+                    modifier = Modifier.testTag(playButtonTag),
+                ) {
+                    Text(text = "Play")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(watchButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(playButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun wideButton_buttonPositioning() {
+        rule.setContent {
+            Box {
+                WideButton(
+                    onClick = { },
+                    modifier = Modifier
+                        .testTag(WideButtonTag),
+                    contentPadding = WideButtonDefaults.ContentPadding,
+                    title = {
+                        Text(
+                            text = "Email",
+                            modifier = Modifier
+                                .testTag(WideButtonTextTag)
+                                .semantics(mergeDescendants = true) {}
+                        )
+                            },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Default.Settings,
+                            contentDescription = "",
+                            modifier = Modifier
+                                .size(WideButtonIconSize)
+                                .testTag(WideButtonIconTag)
+                                .semantics(mergeDescendants = true) {}
+                        )
+                    }
+                )
+            }
+        }
+
+        val buttonBounds = rule.onNodeWithTag(WideButtonTag).getUnclippedBoundsInRoot()
+        val leadingIconBounds = rule.onNodeWithTag(WideButtonIconTag).getUnclippedBoundsInRoot()
+
+        (leadingIconBounds.left - buttonBounds.left).assertIsEqualTo(
+            16.dp,
+            "padding between the start of the button and the start of the leading icon."
+        )
+    }
+}
+
+private val WideButtonIconSize = 18.0.dp
+private const val WideButtonTag = "WideButtonTag"
+private const val WideButtonTextTag = "WideButtonText"
+private const val WideButtonIconTag = "WideButtonIcon"
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
index 75e56dd..560ad85 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
@@ -122,6 +122,47 @@
 }
 
 /**
+ * Defines [Color]s for all TV [Interaction] states of a WideButton
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class WideButtonContentColor internal constructor(
+    internal val contentColor: Color,
+    internal val focusedContentColor: Color,
+    internal val pressedContentColor: Color,
+    internal val disabledContentColor: Color,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as WideButtonContentColor
+
+        if (contentColor != other.contentColor) return false
+        if (focusedContentColor != other.focusedContentColor) return false
+        if (pressedContentColor != other.pressedContentColor) return false
+        if (disabledContentColor != other.disabledContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = contentColor.hashCode()
+        result = 31 * result + focusedContentColor.hashCode()
+        result = 31 * result + pressedContentColor.hashCode()
+        result = 31 * result + disabledContentColor.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "WideButtonContentColor(contentColor=$contentColor, " +
+            "focusedContentColor=$focusedContentColor, " +
+            "pressedContentColor=$pressedContentColor, " +
+            "disabledContentColor=$disabledContentColor)"
+    }
+}
+
+/**
  * Defines the scale for all TV [Interaction] states of Button.
  */
 @ExperimentalTvMaterial3Api
@@ -272,6 +313,15 @@
     )
 
 @OptIn(ExperimentalTvMaterial3Api::class)
+internal fun WideButtonContentColor.toClickableSurfaceContentColor(): ClickableSurfaceColor =
+    ClickableSurfaceColor(
+        color = contentColor,
+        focusedColor = focusedContentColor,
+        pressedColor = pressedContentColor,
+        disabledColor = disabledContentColor,
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
 internal fun ButtonScale.toClickableSurfaceScale() = ClickableSurfaceScale(
     scale = scale,
     focusedScale = focusedScale,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
index 1a5d6c7..53add8d 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
@@ -20,15 +20,26 @@
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.unit.dp
 
 /**
@@ -82,16 +93,328 @@
 }
 
 /**
+ * [ClassicCard] is an opinionated TV Material card that offers a 4 slot layout to show
+ * information about a subject.
+ *
+ * This card has a vertical layout with the interactive surface [Surface], which provides the image
+ * slot at the top, followed by the title, subtitle, and description slots.
+ *
+ * This Card handles click events, calling its [onClick] lambda.
+ *
+ * @param onClick called when this card is clicked
+ * @param image defines the [Composable] image to be displayed on top of the Card.
+ * @param title defines the [Composable] title placed below the image in the Card.
+ * @param modifier the [Modifier] to be applied to this card.
+ * @param subtitle defines the [Composable] supporting text placed below the title of the Card.
+ * @param description defines the [Composable] description placed below the subtitle of the Card.
+ * @param shape [CardShape] defines the shape of this card's container in different interaction
+ * states. See [CardDefaults.shape].
+ * @param colors [CardColors] defines the background & content colors used in this card for
+ * different interaction states. See [CardDefaults.colors].
+ * @param scale [CardScale] defines size of the card relative to its original size for different
+ * interaction states. See [CardDefaults.scale].
+ * @param border [CardBorder] defines a border around the card for different interaction states.
+ * See [CardDefaults.border].
+ * @param glow [CardGlow] defines a shadow to be shown behind the card for different interaction
+ * states. See [CardDefaults.glow].
+ * @param contentPadding [PaddingValues] defines the inner padding applied to the card's content.
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this card. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this card in different states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun ClassicCard(
+    onClick: () -> Unit,
+    image: @Composable BoxScope.() -> Unit,
+    title: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    subtitle: @Composable () -> Unit = {},
+    description: @Composable () -> Unit = {},
+    shape: CardShape = CardDefaults.shape(),
+    colors: CardColors = CardDefaults.colors(),
+    scale: CardScale = CardDefaults.scale(),
+    border: CardBorder = CardDefaults.border(),
+    glow: CardGlow = CardDefaults.glow(),
+    contentPadding: PaddingValues = PaddingValues(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        interactionSource = interactionSource,
+        shape = shape,
+        colors = colors,
+        scale = scale,
+        border = border,
+        glow = glow
+    ) {
+        Column(
+            modifier = Modifier.padding(contentPadding)
+        ) {
+            Box(
+                contentAlignment = CardDefaults.ContentImageAlignment,
+                content = image
+            )
+            Column {
+                CardContent(
+                    title = title,
+                    subtitle = subtitle,
+                    description = description
+                )
+            }
+        }
+    }
+}
+
+/**
+ * [CompactCard] is an opinionated TV Material card that offers a 4 slot layout to show
+ * information about a subject.
+ *
+ * This card provides the interactive surface [Surface] with the image slot as the background
+ * (with an overlay scrim gradient). Other slots for the title, subtitle, and description are
+ * placed over it.
+ *
+ * This Card handles click events, calling its [onClick] lambda.
+ *
+ * @param onClick called when this card is clicked
+ * @param image defines the [Composable] image to be displayed on top of the Card.
+ * @param title defines the [Composable] title placed below the image in the Card.
+ * @param modifier the [Modifier] to be applied to this card.
+ * @param subtitle defines the [Composable] supporting text placed below the title of the Card.
+ * @param description defines the [Composable] description placed below the subtitle of the Card.
+ * @param shape [CardShape] defines the shape of this card's container in different interaction
+ * states. See [CardDefaults.shape].
+ * @param colors [CardColors] defines the background & content colors used in this card for
+ * different interaction states. See [CardDefaults.compactCardColors].
+ * @param scale [CardScale] defines size of the card relative to its original size for different
+ * interaction states. See [CardDefaults.scale].
+ * @param border [CardBorder] defines a border around the card for different interaction states.
+ * See [CardDefaults.border].
+ * @param glow [CardGlow] defines a shadow to be shown behind the card for different interaction
+ * states. See [CardDefaults.glow].
+ * @param scrimBrush [Brush] defines a brush/gradient to be used to draw the scrim over the image
+ * in the background. See [CardDefaults.ContainerGradient].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this card. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this card in different states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun CompactCard(
+    onClick: () -> Unit,
+    image: @Composable BoxScope.() -> Unit,
+    title: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    subtitle: @Composable () -> Unit = {},
+    description: @Composable () -> Unit = {},
+    shape: CardShape = CardDefaults.shape(),
+    colors: CardColors = CardDefaults.compactCardColors(),
+    scale: CardScale = CardDefaults.scale(),
+    border: CardBorder = CardDefaults.border(),
+    glow: CardGlow = CardDefaults.glow(),
+    scrimBrush: Brush = CardDefaults.ContainerGradient,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        interactionSource = interactionSource,
+        shape = shape,
+        colors = colors,
+        scale = scale,
+        border = border,
+        glow = glow
+    ) {
+        Box(contentAlignment = Alignment.BottomStart) {
+            Box(
+                modifier = Modifier
+                    .drawWithCache {
+                        onDrawWithContent {
+                            drawContent()
+                            drawRect(brush = scrimBrush)
+                        }
+                    },
+                contentAlignment = CardDefaults.ContentImageAlignment,
+                content = image
+            )
+            Column {
+                CardContent(
+                    title = title,
+                    subtitle = subtitle,
+                    description = description
+                )
+            }
+        }
+    }
+}
+
+/**
+ * [WideClassicCard] is an opinionated TV Material card that offers a 4 slot layout to show
+ * information about a subject.
+ *
+ * This card has a horizontal layout with the interactive surface [Surface], which provides the
+ * image slot at the start, followed by the title, subtitle, and description slots at the end.
+ *
+ * This Card handles click events, calling its [onClick] lambda.
+ *
+ * @param onClick called when this card is clicked
+ * @param image defines the [Composable] image to be displayed on top of the Card.
+ * @param title defines the [Composable] title placed below the image in the Card.
+ * @param modifier the [Modifier] to be applied to this card.
+ * @param subtitle defines the [Composable] supporting text placed below the title of the Card.
+ * @param description defines the [Composable] description placed below the subtitle of the Card.
+ * @param shape [CardShape] defines the shape of this card's container in different interaction
+ * states. See [CardDefaults.shape].
+ * @param colors [CardColors] defines the background & content colors used in this card for
+ * different interaction states. See [CardDefaults.colors].
+ * @param scale [CardScale] defines size of the card relative to its original size for different
+ * interaction states. See [CardDefaults.scale].
+ * @param border [CardBorder] defines a border around the card for different interaction states.
+ * See [CardDefaults.border].
+ * @param glow [CardGlow] defines a shadow to be shown behind the card for different interaction
+ * states. See [CardDefaults.glow].
+ * @param contentPadding [PaddingValues] defines the inner padding applied to the card's content.
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this card. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this card in different states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun WideClassicCard(
+    onClick: () -> Unit,
+    image: @Composable BoxScope.() -> Unit,
+    title: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    subtitle: @Composable () -> Unit = {},
+    description: @Composable () -> Unit = {},
+    shape: CardShape = CardDefaults.shape(),
+    colors: CardColors = CardDefaults.colors(),
+    scale: CardScale = CardDefaults.scale(),
+    border: CardBorder = CardDefaults.border(),
+    glow: CardGlow = CardDefaults.glow(),
+    contentPadding: PaddingValues = PaddingValues(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        interactionSource = interactionSource,
+        shape = shape,
+        colors = colors,
+        scale = scale,
+        border = border,
+        glow = glow
+    ) {
+        Row(
+            modifier = Modifier.padding(contentPadding)
+        ) {
+            Box(
+                contentAlignment = CardDefaults.ContentImageAlignment,
+                content = image
+            )
+            Column {
+                CardContent(
+                    title = title,
+                    subtitle = subtitle,
+                    description = description
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun CardContent(
+    title: @Composable () -> Unit,
+    subtitle: @Composable () -> Unit = {},
+    description: @Composable () -> Unit = {},
+    contentColor: Color
+) {
+    CompositionLocalProvider(LocalContentColor provides contentColor) {
+        CardContent(title, subtitle, description)
+    }
+}
+
+@Composable
+private fun CardContent(
+    title: @Composable () -> Unit,
+    subtitle: @Composable () -> Unit = {},
+    description: @Composable () -> Unit = {}
+) {
+    ProvideTextStyle(MaterialTheme.typography.titleMedium) {
+        title.invoke()
+    }
+    ProvideTextStyle(MaterialTheme.typography.bodySmall) {
+        Box(Modifier.graphicsLayer { alpha = SubtitleAlpha }) {
+            subtitle.invoke()
+        }
+    }
+    ProvideTextStyle(MaterialTheme.typography.bodySmall) {
+        Box(Modifier.graphicsLayer { alpha = DescriptionAlpha }) {
+            description.invoke()
+        }
+    }
+}
+
+/**
  * Contains the default values used by all card types.
  */
 @ExperimentalTvMaterial3Api
 object CardDefaults {
+    internal val ContentImageAlignment = Alignment.Center
+
     /**
-    * The default [Shape] used by Cards.
-    */
+     * The default [Shape] used by Cards.
+     */
     private val ContainerShape = RoundedCornerShape(8.dp)
 
     /**
+     * Recommended aspect ratio [Float] to get square images, can be applied using the modifier
+     * [Modifier.aspectRatio].
+     */
+    const val SquareImageAspectRatio = 1f
+
+    /**
+     * Recommended aspect ratio [Float] for vertical images, can be applied using the modifier
+     * [Modifier.aspectRatio].
+     */
+    const val VerticalImageAspectRatio = 2f / 3
+
+    /**
+     * Recommended aspect ratio [Float] for horizontal images, can be applied using the modifier
+     * [Modifier.aspectRatio].
+     */
+    const val HorizontalImageAspectRatio = 16f / 9
+
+    /**
+     * Gradient used in cards to give more emphasis to the textual content that is generally
+     * displayed above an image.
+     */
+    val ContainerGradient = Brush.verticalGradient(
+        listOf(
+            Color(red = 28, green = 27, blue = 31, alpha = 0),
+            Color(red = 28, green = 27, blue = 31, alpha = 204)
+        )
+    )
+
+    /**
+     * Returns the content color [Color] from the colors [CardColors] for different
+     * interaction states.
+     */
+    internal fun contentColor(
+        focused: Boolean,
+        pressed: Boolean,
+        colors: CardColors
+    ): Color {
+        return when {
+            focused -> colors.focusedContentColor
+            pressed -> colors.pressedContentColor
+            else -> colors.contentColor
+        }
+    }
+
+    /**
      * Creates a [CardShape] that represents the default container shapes used in a Card.
      *
      * @param shape the default shape used when the Card has no other [Interaction]s.
@@ -121,7 +444,7 @@
     @ReadOnlyComposable
     @Composable
     fun colors(
-        containerColor: Color = MaterialTheme.colorScheme.surface,
+        containerColor: Color = MaterialTheme.colorScheme.surfaceVariant,
         contentColor: Color = contentColorFor(containerColor),
         focusedContainerColor: Color = containerColor,
         focusedContentColor: Color = contentColorFor(focusedContainerColor),
@@ -137,6 +460,34 @@
     )
 
     /**
+     * Creates [CardColors] that represents the default colors used in a Compact Card.
+     *
+     * @param containerColor the default container color of this Card.
+     * @param contentColor the default content color of this Card.
+     * @param focusedContainerColor the container color of this Card when focused.
+     * @param focusedContentColor the content color of this Card when focused.
+     * @param pressedContainerColor the container color of this Card when pressed.
+     * @param pressedContentColor the content color of this Card when pressed.
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun compactCardColors(
+        containerColor: Color = MaterialTheme.colorScheme.surfaceVariant,
+        contentColor: Color = Color.White,
+        focusedContainerColor: Color = containerColor,
+        focusedContentColor: Color = contentColor,
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = focusedContentColor
+    ) = CardColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor
+    )
+
+    /**
      * Creates a [CardScale] that represents the default scales used in a Card.
      * Scales are used to modify the size of a composable in different [Interaction] states
      * e.g. 1f (original) in default state, 1.1f (scaled up) in focused state, 0.8f (scaled down)
@@ -200,6 +551,9 @@
     )
 }
 
+private const val SubtitleAlpha = 0.6f
+private const val DescriptionAlpha = 0.8f
+
 @OptIn(ExperimentalTvMaterial3Api::class)
 private fun CardColors.toClickableSurfaceContainerColor() =
     ClickableSurfaceColor(
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt b/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt
new file mode 100644
index 0000000..30f4d67
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.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.tv.material3
+
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.tokens.Elevation
+
+/**
+ * Material Design wide button for TV.
+ *
+ * @param onClick called when this button is clicked
+ * @param modifier the [Modifier] to be applied to this button
+ * @param enabled controls the enabled state of this button. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param background the background to be applied to the [WideButton]
+ * @param scale Defines size of the Button relative to its original size.
+ * @param glow Shadow to be shown behind the Button.
+ * @param shape Defines the Button's shape.
+ * @param contentColor Color to be used for the text content of the Button
+ * @param tonalElevation tonal elevation used to apply a color shift to the button to give the it
+ * higher emphasis
+ * @param border Defines a border around the Button.
+ * @param contentPadding the spacing values to apply internally between the container and the
+ * content
+ * @param content the content of the button
+ */
+@ExperimentalTvMaterial3Api
+@NonRestartableComposable
+@Composable
+fun WideButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    background: @Composable () -> Unit = {
+        WideButtonDefaults.Background(
+            enabled = enabled,
+            interactionSource = interactionSource,
+        )
+    },
+    scale: ButtonScale = WideButtonDefaults.scale(),
+    glow: ButtonGlow = WideButtonDefaults.glow(),
+    shape: ButtonShape = WideButtonDefaults.shape(),
+    contentColor: WideButtonContentColor = WideButtonDefaults.contentColor(),
+    tonalElevation: Dp = Elevation.Level0,
+    border: ButtonBorder = WideButtonDefaults.border(),
+    contentPadding: PaddingValues = WideButtonDefaults.ContentPadding,
+    content: @Composable RowScope.() -> Unit
+) {
+    WideButtonImpl(
+        onClick = onClick,
+        enabled = enabled,
+        scale = scale,
+        glow = glow,
+        shape = shape,
+        contentColor = contentColor,
+        tonalElevation = tonalElevation,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        modifier = modifier,
+        background = background,
+        content = content
+    )
+}
+
+/**
+ * Material Design wide button for TV.
+ *
+ * @param onClick called when this button is clicked
+ * @param title the title content of the button, typically a [Text]
+ * @param modifier the [Modifier] to be applied to this button
+ * @param enabled controls the enabled state of this button. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param icon the leading icon content of the button, typically an [Icon]
+ * @param subtitle the subtitle content of the button, typically a [Text]
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param background the background to be applied to the [WideButton]
+ * @param scale Defines size of the Button relative to its original size.
+ * @param glow Shadow to be shown behind the Button.
+ * @param shape Defines the Button's shape.
+ * @param contentColor Color to be used for the text content of the Button
+ * @param tonalElevation tonal elevation used to apply a color shift to the button to give the it
+ * higher emphasis
+ * @param border Defines a border around the Button.
+ * @param contentPadding the spacing values to apply internally between the container and the
+ * content
+ */
+@ExperimentalTvMaterial3Api
+@NonRestartableComposable
+@Composable
+fun WideButton(
+    onClick: () -> Unit,
+    title: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    icon: (@Composable () -> Unit)? = null,
+    subtitle: (@Composable () -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    background: @Composable () -> Unit = {
+        WideButtonDefaults.Background(
+            enabled = enabled,
+            interactionSource = interactionSource
+        )
+    },
+    scale: ButtonScale = WideButtonDefaults.scale(),
+    glow: ButtonGlow = WideButtonDefaults.glow(),
+    shape: ButtonShape = WideButtonDefaults.shape(),
+    contentColor: WideButtonContentColor = WideButtonDefaults.contentColor(),
+    tonalElevation: Dp = Elevation.Level0,
+    border: ButtonBorder = WideButtonDefaults.border(),
+    contentPadding: PaddingValues = WideButtonDefaults.ContentPadding,
+) {
+
+    WideButtonImpl(
+        onClick = onClick,
+        enabled = enabled,
+        scale = scale,
+        glow = glow,
+        shape = shape,
+        contentColor = contentColor,
+        tonalElevation = tonalElevation,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        modifier = modifier,
+        minHeight = if (subtitle == null)
+            BaseWideButtonDefaults.MinHeight
+        else
+            BaseWideButtonDefaults.MinHeightWithSubtitle,
+        background = background
+    ) {
+        if (icon != null) {
+            icon()
+            Spacer(
+                modifier = Modifier.padding(end = BaseWideButtonDefaults.HorizontalContentGap)
+            )
+        }
+        Column {
+            ProvideTextStyle(
+                value = MaterialTheme.typography.titleMedium,
+                content = {
+                    Box(
+                        modifier = Modifier
+                            .padding(vertical = BaseWideButtonDefaults.VerticalContentGap)
+                    ) {
+                        title()
+                    }
+                }
+            )
+            if (subtitle != null) {
+                ProvideTextStyle(
+                    value = MaterialTheme.typography.bodySmall.copy(
+                        color = LocalContentColor.current.copy(
+                            alpha = BaseWideButtonDefaults.SubtitleAlpha
+                        )
+                    ),
+                    content = subtitle
+                )
+            }
+        }
+    }
+}
+
+@ExperimentalTvMaterial3Api
+@Composable
+private fun WideButtonImpl(
+    onClick: () -> Unit,
+    enabled: Boolean,
+    scale: ButtonScale,
+    glow: ButtonGlow,
+    shape: ButtonShape,
+    contentColor: WideButtonContentColor,
+    tonalElevation: Dp,
+    border: ButtonBorder,
+    contentPadding: PaddingValues,
+    interactionSource: MutableInteractionSource,
+    background: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    minHeight: Dp = BaseWideButtonDefaults.MinHeight,
+    content: @Composable RowScope.() -> Unit
+) {
+    val density = LocalDensity.current
+    var buttonWidth by remember { mutableStateOf(0.dp) }
+    var buttonHeight by remember { mutableStateOf(0.dp) }
+
+    Surface(
+        modifier = modifier.semantics { role = Role.Button },
+        onClick = onClick,
+        enabled = enabled,
+        scale = scale.toClickableSurfaceScale(),
+        glow = glow.toClickableSurfaceGlow(),
+        shape = shape.toClickableSurfaceShape(),
+        color = wideButtonContainerColor(),
+        contentColor = contentColor.toClickableSurfaceContentColor(),
+        tonalElevation = tonalElevation,
+        border = border.toClickableSurfaceBorder(),
+        interactionSource = interactionSource
+    ) {
+        ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
+            Box(
+                modifier = Modifier
+                    .defaultMinSize(
+                        minWidth = BaseWideButtonDefaults.MinWidth,
+                        minHeight = minHeight,
+                    )
+                    .onPlaced {
+                        with(density) {
+                            buttonWidth = it.size.width.toDp()
+                            buttonHeight = it.size.height.toDp()
+                        }
+                    }
+            ) {
+                Box(modifier = Modifier.size(buttonWidth, buttonHeight)) {
+                    background()
+                }
+
+                Row(
+                    modifier = Modifier
+                        .size(buttonWidth, buttonHeight)
+                        .padding(contentPadding),
+                    verticalAlignment = Alignment.CenterVertically,
+                    content = content
+                )
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+private fun wideButtonContainerColor() = ClickableSurfaceDefaults.color(
+    color = Color.Transparent,
+    focusedColor = Color.Transparent,
+    pressedColor = Color.Transparent,
+    disabledColor = Color.Transparent,
+)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/WideButtonDefaults.kt b/tv/tv-material/src/main/java/androidx/tv/material3/WideButtonDefaults.kt
new file mode 100644
index 0000000..07402ff
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/WideButtonDefaults.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.annotation.FloatRange
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.dp
+
+internal object BaseWideButtonDefaults {
+    const val SubtitleAlpha = 0.8f
+    val MinWidth = 240.dp
+    val MinHeight = 48.dp
+    val MinHeightWithSubtitle = 64.dp
+    val HorizontalContentGap = 12.dp
+    val VerticalContentGap = 4.dp
+}
+
+@ExperimentalTvMaterial3Api
+object WideButtonDefaults {
+    private val HorizontalPadding = 16.dp
+    private val VerticalPadding = 10.dp
+
+    /** The default content padding used by [WideButton] */
+    internal val ContentPadding = PaddingValues(
+        start = HorizontalPadding,
+        top = VerticalPadding,
+        end = HorizontalPadding,
+        bottom = VerticalPadding
+    )
+
+    private val ContainerShape = RoundedCornerShape(12.dp)
+
+    /**
+     * Default background for a [WideButton]
+     */
+    @Composable
+    fun Background(
+        enabled: Boolean,
+        interactionSource: MutableInteractionSource,
+    ) {
+        val isFocused = interactionSource.collectIsFocusedAsState().value
+        val isPressed = interactionSource.collectIsPressedAsState().value
+
+        val backgroundColor = when {
+            !enabled -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
+            isPressed -> MaterialTheme.colorScheme.onSurface
+            isFocused -> MaterialTheme.colorScheme.onSurface
+            else -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
+        }
+
+        Box(modifier = Modifier
+            .fillMaxSize()
+            .background(backgroundColor))
+    }
+
+    /**
+     * Creates a [ButtonShape] that represents the default container shapes used in a [WideButton]
+     *
+     * @param shape the shape used when the Button is enabled, and has no other [Interaction]s
+     * @param focusedShape the shape used when the Button is enabled and focused
+     * @param pressedShape the shape used when the Button is enabled pressed
+     * @param disabledShape the shape used when the Button is not enabled
+     * @param focusedDisabledShape the shape used when the Button is not enabled and focused
+     */
+    fun shape(
+        shape: Shape = ContainerShape,
+        focusedShape: Shape = shape,
+        pressedShape: Shape = shape,
+        disabledShape: Shape = shape,
+        focusedDisabledShape: Shape = disabledShape
+    ) = ButtonShape(
+        shape = shape,
+        focusedShape = focusedShape,
+        pressedShape = pressedShape,
+        disabledShape = disabledShape,
+        focusedDisabledShape = focusedDisabledShape
+    )
+
+    /**
+     * Creates a [WideButtonContentColor] that represents the default content colors used in a
+     * [WideButton]
+     *
+     * @param color the content color of this Button when enabled
+     * @param focusedColor the content color of this Button when enabled and focused
+     * @param pressedColor the content color of this Button when enabled and pressed
+     * @param disabledColor the content color of this Button when not enabled
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun contentColor(
+        color: Color = MaterialTheme.colorScheme.onSurface,
+        focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
+        pressedColor: Color = focusedColor,
+        disabledColor: Color = color
+    ) = WideButtonContentColor(
+        contentColor = color,
+        focusedContentColor = focusedColor,
+        pressedContentColor = pressedColor,
+        disabledContentColor = disabledColor
+    )
+
+    /**
+     * Creates a [ButtonScale] that represents the default scales used in a [WideButton].
+     * Scale is used to modify the size of a composable in different [Interaction]
+     * states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
+     * 0.8f (scaled down) in pressed state, etc.
+     *
+     * @param scale the scale to be used for this Button when enabled
+     * @param focusedScale the scale to be used for this Button when focused
+     * @param pressedScale the scale to be used for this Button when pressed
+     * @param disabledScale the scale to be used for this Button when disabled
+     * @param focusedDisabledScale the scale to be used for this Button when disabled and
+     * focused
+     */
+    fun scale(
+        @FloatRange(from = 0.0) scale: Float = 1f,
+        @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
+        @FloatRange(from = 0.0) pressedScale: Float = scale,
+        @FloatRange(from = 0.0) disabledScale: Float = scale,
+        @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
+    ) = ButtonScale(
+        scale = scale,
+        focusedScale = focusedScale,
+        pressedScale = pressedScale,
+        disabledScale = disabledScale,
+        focusedDisabledScale = focusedDisabledScale
+    )
+
+    /**
+     * Creates a [ButtonBorder] that represents the default [Border]s applied on a
+     * [WideButton] in different [Interaction] states
+     *
+     * @param border the [Border] to be used for this Button when enabled
+     * @param focusedBorder the [Border] to be used for this Button when focused
+     * @param pressedBorder the [Border] to be used for this Button when pressed
+     * @param disabledBorder the [Border] to be used for this Button when disabled
+     * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
+     * focused
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun border(
+        border: Border = Border.None,
+        focusedBorder: Border = border,
+        pressedBorder: Border = focusedBorder,
+        disabledBorder: Border = border,
+        focusedDisabledBorder: Border = Border(
+            border = BorderStroke(
+                width = 2.dp,
+                color = MaterialTheme.colorScheme.border
+            ),
+            inset = 0.dp,
+            shape = ContainerShape
+        )
+    ) = ButtonBorder(
+        border = border,
+        focusedBorder = focusedBorder,
+        pressedBorder = pressedBorder,
+        disabledBorder = disabledBorder,
+        focusedDisabledBorder = focusedDisabledBorder
+    )
+
+    /**
+     * Creates a [ButtonGlow] that represents the default [Glow]s used in a [WideButton]
+     *
+     * @param glow the Glow behind this Button when enabled
+     * @param focusedGlow the Glow behind this Button when focused
+     * @param pressedGlow the Glow behind this Button when pressed
+     */
+    fun glow(
+        glow: Glow = Glow.None,
+        focusedGlow: Glow = glow,
+        pressedGlow: Glow = glow
+    ) = ButtonGlow(
+        glow = glow,
+        focusedGlow = focusedGlow,
+        pressedGlow = pressedGlow
+    )
+}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
index 739ee87..201cc38 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
@@ -195,19 +195,19 @@
             return Collections.emptyList();
         }
 
-        Cursor cursor = mContext.getContentResolver()
+        List<PreviewChannel> channels = new ArrayList<>();
+        try (Cursor cursor = mContext.getContentResolver()
                 .query(
                         TvContractCompat.Channels.CONTENT_URI,
                         PreviewChannel.Columns.PROJECTION,
                         null,
                         null,
-                        null);
-
-        List<PreviewChannel> channels = new ArrayList<>();
-        if (cursor != null && cursor.moveToFirst()) {
-            do {
-                channels.add(PreviewChannel.fromCursor(cursor));
-            } while (cursor.moveToNext());
+                        null)) {
+            if (cursor != null && cursor.moveToFirst()) {
+                do {
+                    channels.add(PreviewChannel.fromCursor(cursor));
+                } while (cursor.moveToNext());
+            }
         }
         return channels;
     }
@@ -228,10 +228,15 @@
 
         PreviewChannel channel = null;
         Uri channelUri = TvContractCompat.buildChannelUri(channelId);
-        Cursor cursor = mContext.getContentResolver()
-                .query(channelUri, PreviewChannel.Columns.PROJECTION, null, null, null);
-        if (cursor != null && cursor.moveToFirst()) {
-            channel = PreviewChannel.fromCursor(cursor);
+        try (Cursor cursor = mContext.getContentResolver()
+                .query(channelUri,
+                        PreviewChannel.Columns.PROJECTION,
+                        null,
+                        null,
+                        null)) {
+            if (cursor != null && cursor.moveToFirst()) {
+                channel = PreviewChannel.fromCursor(cursor);
+            }
         }
         return channel;
     }
@@ -416,9 +421,12 @@
 
         PreviewProgram program = null;
         Uri programUri = TvContractCompat.buildPreviewProgramUri(programId);
-        Cursor cursor = mContext.getContentResolver().query(programUri, null, null, null, null);
-        if (cursor != null && cursor.moveToFirst()) {
-            program = PreviewProgram.fromCursor(cursor);
+        try (Cursor cursor = mContext.getContentResolver()
+                .query(programUri, null, null, null, null);
+        ) {
+            if (cursor != null && cursor.moveToFirst()) {
+                program = PreviewProgram.fromCursor(cursor);
+            }
         }
         return program;
     }
@@ -492,9 +500,12 @@
 
         WatchNextProgram program = null;
         Uri programUri = TvContractCompat.buildWatchNextProgramUri(programId);
-        Cursor cursor = mContext.getContentResolver().query(programUri, null, null, null, null);
-        if (cursor != null && cursor.moveToFirst()) {
-            program = WatchNextProgram.fromCursor(cursor);
+        try (Cursor cursor = mContext.getContentResolver()
+                .query(programUri, null, null, null, null)
+        ) {
+            if (cursor != null && cursor.moveToFirst()) {
+                program = WatchNextProgram.fromCursor(cursor);
+            }
         }
         return program;
     }
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index f1d47a0..478b99e 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -26,8 +26,8 @@
         applicationId "androidx.wear.compose.integration.demos"
         minSdk 25
         targetSdk 30
-        versionCode 13
-        versionName "1.13"
+        versionCode 14
+        versionName "1.14"
         // Change the APK name to match the *testapp regex we use to pick up APKs for testing as
         // part of CI.
         archivesBaseName = "wear-compose-demos-testapp"
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutTheme.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutTheme.java
index 74c0a40..b6836ab 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutTheme.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutTheme.java
@@ -24,10 +24,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 
-/**
- * Theme customization for ProtoLayout texts, which includes Font types and variants.
- *
- */
+/** Theme customization for ProtoLayout texts, which includes Font types and variants. */
 @RestrictTo(Scope.LIBRARY)
 public interface ProtoLayoutTheme {
 
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutVisibilityState.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutVisibilityState.java
index 214535a..64654d5 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutVisibilityState.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutVisibilityState.java
@@ -23,10 +23,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-/**
- * The visibility state of the layout.
- *
- */
+/** The visibility state of the layout. */
 @IntDef({
     ProtoLayoutVisibilityState.VISIBILITY_STATE_FULLY_VISIBLE,
     ProtoLayoutVisibilityState.VISIBILITY_STATE_PARTIALLY_VISIBLE,
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
index e5a30d5..906634e 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
@@ -35,16 +35,13 @@
 import java.util.Collections;
 import java.util.List;
 
-/**
- * Utility to diff 2 proto layouts in order to be able to partially update the display.
- *
- */
+/** Utility to diff 2 proto layouts in order to be able to partially update the display. */
 @RestrictTo(Scope.LIBRARY_GROUP)
 public class ProtoLayoutDiffer {
     /** Prefix for all node IDs generated by this differ. */
     @NonNull private static final String NODE_ID_PREFIX = "pT";
 
-    /** Node ID of the root node. @hide */
+    /** Node ID of the root node. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
     public static final String ROOT_NODE_ID = NODE_ID_PREFIX + "1";
@@ -55,7 +52,6 @@
     /**
      * If true, an element addition or removal forces its parent (and siblings of the changed node)
      * to reinflate.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final boolean UPDATE_ALL_CHILDREN_AFTER_ADD_REMOVE = true;
@@ -63,7 +59,6 @@
     /**
      * Index of the first child node under a parent. {@link #createNodePosId} should be called
      * starting from this value and incremented by one for each child node.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final int FIRST_CHILD_INDEX = 0;
@@ -118,10 +113,7 @@
         }
     }
 
-    /**
-     * A node in a layout tree, that has a change compared to a previous version.
-     *
-     */
+    /** A node in a layout tree, that has a change compared to a previous version. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class TreeNodeWithChange {
         @NonNull private final TreeNode mTreeNode;
@@ -135,7 +127,6 @@
         /**
          * Returns the linear {@link LayoutElement} that this node represents, or null if the node
          * isn't for a {@link LayoutElement}.
-         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -146,7 +137,6 @@
         /**
          * Returns the radial {@link ArcLayoutElement} that this node represents, or null if the
          * node isn't for a {@link ArcLayoutElement}.
-         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -154,7 +144,7 @@
             return mTreeNode.mArcLayoutElement;
         }
 
-        /** Returns the fingerprint for this node. @hide */
+        /** Returns the fingerprint for this node. */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         public NodeFingerprint getFingerprint() {
@@ -164,7 +154,6 @@
         /**
          * Returns an ID for this node based on its position in the tree. Only comparable against
          * other position IDs that are generated with {@link #createNodePosId}.
-         *
          */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -175,7 +164,6 @@
         /**
          * Returns true if the change in this node affects the node itself only. Otherwise the
          * change affects both the node and its children.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         public boolean isSelfOnlyChange() {
@@ -183,7 +171,7 @@
         }
     }
 
-    /** A diff in layout, containing information about the tree nodes that have changed. @hide */
+    /** A diff in layout, containing information about the tree nodes that have changed. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class LayoutDiff {
         @NonNull private final List<TreeNodeWithChange> mChangedNodes;
@@ -195,7 +183,6 @@
         /**
          * An ordered list of nodes that have changed. A changed node always comes before its
          * changed descendants in this list.
-         *
          */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -285,7 +272,7 @@
         return new LayoutDiff(changedNodes);
     }
 
-    /** Check whether 2 nodes represented by the given fingerprints are equivalent. @hide */
+    /** Check whether 2 nodes represented by the given fingerprints are equivalent. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static boolean areNodesEquivalent(
             @NonNull NodeFingerprint nodeA, @NonNull NodeFingerprint nodeB) {
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
index 20538ab..3a0ce8f 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
@@ -123,10 +123,7 @@
         mResolvedAvds.forEach(ResolvedAvd::unregisterCallback);
     }
 
-    /**
-     * Returns the number of active bound dynamic types.
-     *
-     */
+    /** Returns the number of active bound dynamic types. */
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     @SuppressWarnings("RestrictTo")
     int size() {
@@ -204,10 +201,7 @@
         return null;
     }
 
-    /**
-     * Returns how many animations are running.
-     *
-     */
+    /** Returns how many animations are running. */
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     @SuppressWarnings("RestrictTo")
     int getRunningAnimationCount() {
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 e270735..fc45546 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
@@ -74,7 +74,6 @@
  *
  * <p>Given a dynamic ProtoLayout data source, this builds up a {@link BoundDynamicType}, which can
  * source the required data, and transform it into its final form.
- *
  */
 @RestrictTo(Scope.LIBRARY_GROUP)
 public class ProtoLayoutDynamicDataPipeline {
@@ -92,10 +91,7 @@
     @NonNull final QuotaManager mAnimationQuotaManager;
     @NonNull private final DynamicTypeEvaluator mEvaluator;
 
-    /**
-     * Creates a {@link ProtoLayoutDynamicDataPipeline} without animation support.
-     *
-     */
+    /** Creates a {@link ProtoLayoutDynamicDataPipeline} without animation support. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public ProtoLayoutDynamicDataPipeline(
             boolean canUpdateGateways,
@@ -113,7 +109,6 @@
     /**
      * Creates a {@link ProtoLayoutDynamicDataPipeline} with animation support. Maximum number of
      * concurrently running animations is defined in the given {@link QuotaManager}.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public ProtoLayoutDynamicDataPipeline(
@@ -151,10 +146,7 @@
         this.mEvaluator = new DynamicTypeEvaluator(evaluatorConfigBuilder.build());
     }
 
-    /**
-     * Returns the number of active dynamic types in this pipeline.
-     *
-     */
+    /** Returns the number of active dynamic types in this pipeline. */
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     @RestrictTo(Scope.TESTS)
     public int size() {
@@ -172,10 +164,7 @@
         mPositionIdTree.removeChildNodesFor(posId);
     }
 
-    /**
-     * Build {@link PipelineMaker}.
-     *
-     */
+    /** Build {@link PipelineMaker}. */
     @NonNull
     @RestrictTo(Scope.LIBRARY_GROUP)
     public PipelineMaker newPipelineMaker(
@@ -187,7 +176,6 @@
     /**
      * Test version of the {@link #newPipelineMaker(BiFunction, BiFunction)} without animation
      * inflators.
-     *
      */
     @VisibleForTesting
     @NonNull
@@ -201,7 +189,6 @@
     /**
      * Sets whether this proto layout can perform updates. If the proto layout cannot update, then
      * updates through the data pipeline (e.g. health updates) will be suppressed.
-     *
      */
     @UiThread
     @SuppressWarnings("RestrictTo")
@@ -214,10 +201,7 @@
         }
     }
 
-    /**
-     * Closes existing gateways.
-     *
-     */
+    /** Closes existing gateways. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @SuppressWarnings("RestrictTo")
     public void close() {
@@ -234,7 +218,6 @@
      * <p>The nodes are accumulated and can be committed to the pipeline.
      *
      * <p>Note that this class is not thread-safe.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class PipelineMaker {
@@ -464,7 +447,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -483,7 +465,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -499,7 +480,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -516,7 +496,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -533,7 +512,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -550,7 +528,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -566,7 +543,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -583,7 +559,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -599,7 +574,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -616,7 +590,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -633,7 +606,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -649,7 +621,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @SuppressWarnings("RestrictTo")
         @NonNull
@@ -666,7 +637,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -683,7 +653,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -700,7 +669,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -716,7 +684,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -733,7 +700,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressWarnings("RestrictTo")
@@ -747,10 +713,7 @@
                     colorProp, posId, buildStateUpdateCallback(invalidData, consumer));
         }
 
-        /**
-         * This store method shall be called during the layout inflation in a background thread.
-         *
-         */
+        /** This store method shall be called during the layout inflation in a background thread. */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @SuppressLint("CheckReturnValue") // (b/247804720)
         @NonNull
@@ -770,7 +733,6 @@
         /**
          * This store method shall be called during the layout inflation in a background thread. It
          * adds given {@link DynamicBool} to the pipeline too.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -794,10 +756,7 @@
             return this;
         }
 
-        /**
-         * This store method shall be called during the layout inflation in a background thread.
-         *
-         */
+        /** This store method shall be called during the layout inflation in a background thread. */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         public PipelineMaker addResolvedSeekableAnimatedImage(
@@ -825,10 +784,7 @@
             return this;
         }
 
-        /**
-         * Stores the {@link AnimatedVisibility} associated with the {@code posId}.
-         *
-         */
+        /** Stores the {@link AnimatedVisibility} associated with the {@code posId}. */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         public PipelineMaker storeAnimatedVisibilityFor(
@@ -899,7 +855,6 @@
         /**
          * Add the given source to the pipeline for future evaluation. Evaluation will start when
          * {@link PipelineMaker} is committed with {@link PipelineMaker#commit}.
-         *
          */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -908,10 +863,7 @@
             return this;
         }
 
-        /**
-         * Stores a node if doesn't exist. Otherwise does nothing.
-         *
-         */
+        /** Stores a node if doesn't exist. Otherwise does nothing. */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
         public PipelineMaker rememberNode(@NonNull String nodePosId) {
@@ -928,7 +880,6 @@
      *
      * <p>This method can be called directly in screenshot tests and when the renderer output is
      * never supposed to be attached to a window.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @UiThread
@@ -949,10 +900,7 @@
         mPositionIdTree.forEach(NodeInfo::initPendingBoundTypes);
     }
 
-    /**
-     * Play the animation with the given trigger type.
-     *
-     */
+    /** Play the animation with the given trigger type. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public void playAvdAnimations(@NonNull Trigger.InnerCase triggerCase) {
@@ -965,10 +913,7 @@
         mPositionIdTree.forEach(info -> info.setVisibility(visible));
     }
 
-    /**
-     * Reset the avd animations with the given trigger type.
-     *
-     */
+    /** Reset the avd animations with the given trigger type. */
     @UiThread
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -976,10 +921,7 @@
         mPositionIdTree.forEach(info -> info.resetAvdAnimations(triggerCase));
     }
 
-    /**
-     * Stops running avd animations and releases their quota.
-     *
-     */
+    /** Stops running avd animations and releases their quota. */
     @UiThread
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -999,7 +941,6 @@
     /**
      * Sets visibility for resources tracked by the pipeline and plays / stops any affected
      * animations.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @UiThread
@@ -1031,7 +972,6 @@
     /**
      * Returns the total duration in milliseconds of the animated drawable associated with a
      * StateSource with the given key name; or null if no such SourceKey exists.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Nullable
@@ -1064,10 +1004,7 @@
         return affectedNodes;
     }
 
-    /**
-     * Returns how many animations are running.
-     *
-     */
+    /** Returns how many animations are running. */
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     @RestrictTo(Scope.TESTS)
     public int getRunningAnimationsCount() {
@@ -1082,10 +1019,7 @@
                         .sum();
     }
 
-    /**
-     * Returns whether all quota has been released.
-     *
-     */
+    /** Returns whether all quota has been released. */
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     @RestrictTo(Scope.TESTS)
     public boolean isAllQuotaReleased() {
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ContentUriValidator.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ContentUriValidator.java
index 99a1b281..e126046 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ContentUriValidator.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ContentUriValidator.java
@@ -1,3 +1,19 @@
+/*
+ * 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.protolayout.renderer.inflater;
 
 import android.content.Context;
@@ -91,7 +107,7 @@
             return false;
         }
 
-        // Otherwise, only allow content from the same package that provided the layout.
+        // Otherwise, only allow content from the same package that provided the tile.
         return providerInfo.packageName.equals(mAllowedPackageName);
     }
 
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByContentUriResolver.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByContentUriResolver.java
index a19a08e..14e2fa6 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByContentUriResolver.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByContentUriResolver.java
@@ -1,3 +1,19 @@
+/*
+ * 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.protolayout.renderer.inflater;
 
 import android.content.ContentResolver;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByResIdResolver.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByResIdResolver.java
index 5b343d6..0989b25 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByResIdResolver.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidImageResourceByResIdResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * 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.
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultInlineImageResourceResolver.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultInlineImageResourceResolver.java
index 8081c5b..b5714cc 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultInlineImageResourceResolver.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultInlineImageResourceResolver.java
@@ -34,9 +34,7 @@
 import java.nio.ByteBuffer;
 
 /** Resource resolver for inline resources. */
-// TODO(b/276703002): Add support for ARGB_8888 images.
 public class DefaultInlineImageResourceResolver implements InlineImageResourceResolver {
-    private static final int RGB565_BYTES_PER_PX = 2;
     private static final String TAG = "InlineImageResolver";
 
     @NonNull private final Context mAppContext;
@@ -52,7 +50,8 @@
             throws ResourceAccessException {
         @Nullable Bitmap bitmap = null;
 
-        if (inlineImage.getFormat() == ImageFormat.IMAGE_FORMAT_RGB_565) {
+        if (inlineImage.getFormat() == ImageFormat.IMAGE_FORMAT_RGB_565
+                || inlineImage.getFormat() == ImageFormat.IMAGE_FORMAT_ARGB_8888) {
             bitmap = loadRawBitmap(inlineImage);
         } else if (inlineImage.getFormat() == ImageFormat.IMAGE_FORMAT_UNDEFINED) {
             bitmap = loadStructuredBitmap(inlineImage);
@@ -72,29 +71,38 @@
         switch (imageFormat) {
             case IMAGE_FORMAT_RGB_565:
                 return Config.RGB_565;
+            case IMAGE_FORMAT_ARGB_8888:
+                return Config.ARGB_8888;
             case IMAGE_FORMAT_UNDEFINED:
             case UNRECOGNIZED:
-            case IMAGE_FORMAT_ARGB_8888:
                 return null;
         }
-
         return null;
     }
 
+    private int getBytesPerPixel(Config config) {
+        if (config == Config.RGB_565) {
+            return 2;
+        } else if (config == Config.ARGB_8888) {
+            return 4;
+        }
+        return -1;
+    }
+
     @NonNull
     private Bitmap loadRawBitmap(@NonNull InlineImageResource inlineImage)
             throws ResourceAccessException {
         Config config = imageFormatToBitmapConfig(inlineImage.getFormat());
 
-        // Only handles RGB_565 for now
-        if (config != Config.RGB_565) {
+        if (config == null) {
             throw new ResourceAccessException("Unknown image format in image resource.");
         }
 
         int widthPx = inlineImage.getWidthPx();
         int heightPx = inlineImage.getHeightPx();
 
-        int expectedDataSize = widthPx * heightPx * RGB565_BYTES_PER_PX;
+        int bytesPerPixel = getBytesPerPixel(config);
+        int expectedDataSize = widthPx * heightPx * bytesPerPixel;
         if (inlineImage.getData().size() != expectedDataSize) {
             throw new ResourceAccessException(
                     "Mismatch between image data size and dimensions in image resource.");
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RatioViewWrapper.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RatioViewWrapper.java
index 82e2803..9cca584 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RatioViewWrapper.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RatioViewWrapper.java
@@ -165,7 +165,7 @@
                 // This should have been picked up by the aspect ratio check above...
                 throw new IllegalStateException(
                         "Neither target width nor target height was smaller than measured"
-                            + " width/height");
+                                + " width/height");
             }
         } else if (widthMeasureMode == MeasureSpec.EXACTLY) {
             // Can't change the width, but can change height.
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ResourceResolvers.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ResourceResolvers.java
index 0b59ad9..2434d9c 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ResourceResolvers.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ResourceResolvers.java
@@ -39,7 +39,7 @@
  * common resolver implementations.
  */
 public class ResourceResolvers {
-    private final ResourceProto.Resources mProtoResources;
+    @NonNull private final ResourceProto.Resources mProtoResources;
 
     @Nullable
     private final AndroidImageResourceByResIdResolver mAndroidImageResourceByResIdResolver;
@@ -206,16 +206,18 @@
                 mProtoResources.getIdToImageMap().get(protoResourceId);
 
         if (imageResource == null) {
-            return Futures.immediateFailedFuture(new IllegalArgumentException(
-                                "Resource " + protoResourceId + " is not defined in resources bundle"));
+            return Futures.immediateFailedFuture(
+                    new IllegalArgumentException(
+                            "Resource " + protoResourceId + " is not defined in resources bundle"));
         }
 
         @Nullable
         ListenableFuture<Drawable> drawableFutureOrNull =
                 getDrawableForImageResource(imageResource);
         if (drawableFutureOrNull == null) {
-            return Futures.immediateFailedFuture(new ResourceAccessException(
-                                "Can't find resolver for image resource " + protoResourceId));
+            return Futures.immediateFailedFuture(
+                    new ResourceAccessException(
+                            "Can't find resolver for image resource " + protoResourceId));
         }
         return drawableFutureOrNull;
     }
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/SizedArcContainer.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/SizedArcContainer.java
index ad1cab0..fdd7bd4 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/SizedArcContainer.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/SizedArcContainer.java
@@ -93,8 +93,7 @@
         this(context, attrs, 0);
     }
 
-    SizedArcContainer(
-            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    SizedArcContainer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/StandardResourceResolvers.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/StandardResourceResolvers.java
index 963e769..b9ae624 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/StandardResourceResolvers.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/StandardResourceResolvers.java
@@ -16,6 +16,7 @@
 
 package androidx.wear.protolayout.renderer.inflater;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
 
@@ -37,6 +38,7 @@
      * @param appContext Context for the app that both owns the resources and displays the layout.
      * @param asyncLoadExecutor The executor to use to load any async resources (e.g. Content URI).
      */
+    @SuppressLint("CheckResult")
     @NonNull
     public static ResourceResolvers.Builder forLocalApp(
             @NonNull ResourceProto.Resources protoResources,
@@ -88,7 +90,8 @@
     }
 
     /**
-     * Get a builder pre-populated with resolvers resources hosted within another app on the device.
+     * Get a builder pre-populated with resolvers for the resources of a {@link TileService}, hosted
+     * within another app on the device.
      *
      * <p>Use {@code setFooAccessor} calls to change the pre-populated ones or add others.
      *
@@ -100,6 +103,7 @@
      * @param animationEnabled Whether animation is enabled, which decides whether to load AVD
      *     resources.
      */
+    @SuppressLint("CheckResult")
     @NonNull
     public static ResourceResolvers.Builder forRemoteService(
             @NonNull ResourceProto.Resources protoResources,
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/WearCurvedLineView.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/WearCurvedLineView.java
index 473e071..e05d2cf 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/WearCurvedLineView.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/WearCurvedLineView.java
@@ -242,7 +242,8 @@
     }
 
     /** Returns the stockCap of this arc. */
-    @NonNull public Cap getStrokeCap() {
+    @NonNull
+    public Cap getStrokeCap() {
         return mCap;
     }
 
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 300a829..edc3db4 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
@@ -551,7 +551,8 @@
                     ComplicationType.LONG_TEXT,
                     ComplicationType.SHORT_TEXT,
                     ComplicationType.MONOCHROMATIC_IMAGE,
-                    ComplicationType.SMALL_IMAGE
+                    ComplicationType.SMALL_IMAGE,
+                    ComplicationType.PHOTO_IMAGE
                 ),
                 DefaultComplicationDataSourcePolicy(
                     ComponentName("com.primary.package", "com.primary.app"),
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index b92c323..0def254 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -724,6 +724,10 @@
                 ".systemDataSourceFallbackDefaultType."
         )
         public fun setDefaultDataSourceType(defaultDataSourceType: ComplicationType): Builder {
+            require(defaultDataSourceType in supportedTypes) {
+                "Can't set $defaultDataSourceType because it's not in the supportedTypes list:" +
+                    " $supportedTypes"
+            }
             defaultDataSourcePolicy =
                 when {
                     defaultDataSourcePolicy.secondaryDataSource != null ->
@@ -803,8 +807,33 @@
         }
 
         /** Constructs the [ComplicationSlot]. */
-        public fun build(): ComplicationSlot =
-            ComplicationSlot(
+        public fun build(): ComplicationSlot {
+            require(defaultDataSourcePolicy.primaryDataSourceDefaultType == null ||
+                defaultDataSourcePolicy.primaryDataSourceDefaultType in supportedTypes
+            ) {
+                "defaultDataSourcePolicy.primaryDataSourceDefaultType " +
+                    "${defaultDataSourcePolicy.primaryDataSourceDefaultType} must be in the" +
+                    " supportedTypes list: $supportedTypes"
+            }
+
+            require(defaultDataSourcePolicy.secondaryDataSourceDefaultType == null ||
+                defaultDataSourcePolicy.secondaryDataSourceDefaultType in supportedTypes
+            ) {
+                "defaultDataSourcePolicy.secondaryDataSourceDefaultType " +
+                    "${defaultDataSourcePolicy.secondaryDataSourceDefaultType} must be in the" +
+                    " supportedTypes list: $supportedTypes"
+            }
+
+            require(defaultDataSourcePolicy.systemDataSourceFallbackDefaultType ==
+                ComplicationType.NOT_CONFIGURED ||
+                defaultDataSourcePolicy.systemDataSourceFallbackDefaultType in supportedTypes
+            ) {
+                "defaultDataSourcePolicy.systemDataSourceFallbackDefaultType " +
+                    "${defaultDataSourcePolicy.systemDataSourceFallbackDefaultType} must be in " +
+                    "the supportedTypes list: $supportedTypes"
+            }
+
+            return ComplicationSlot(
                 id,
                 accessibilityTraversalIndex,
                 boundsType,
@@ -821,6 +850,7 @@
                 screenReaderNameResourceId,
                 boundingArc
             )
+        }
     }
 
     internal interface InvalidateListener {
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 97ada96..e7f10f7 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -1079,7 +1079,7 @@
                     { watchState, listener ->
                         CanvasComplicationDrawable(complicationDrawableLeft, watchState, listener)
                     },
-                    emptyList(),
+                    listOf(ComplicationType.SHORT_TEXT),
                     DefaultComplicationDataSourcePolicy(
                         SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
                     ),
@@ -1094,7 +1094,7 @@
                     { watchState, listener ->
                         CanvasComplicationDrawable(complicationDrawableLeft, watchState, listener)
                     },
-                    emptyList(),
+                    listOf(ComplicationType.SHORT_TEXT),
                     DefaultComplicationDataSourcePolicy(
                         SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
                     ),
@@ -1109,7 +1109,7 @@
                     { watchState, listener ->
                         CanvasComplicationDrawable(complicationDrawableLeft, watchState, listener)
                     },
-                    emptyList(),
+                    listOf(ComplicationType.SHORT_TEXT),
                     DefaultComplicationDataSourcePolicy(
                         SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
                     ),
@@ -2146,7 +2146,7 @@
                     { watchState, listener ->
                         CanvasComplicationDrawable(complicationDrawableLeft, watchState, listener)
                     },
-                    emptyList(),
+                    listOf(ComplicationType.SHORT_TEXT),
                     DefaultComplicationDataSourcePolicy(
                         dataSource1,
                         dataSource2,
@@ -2181,7 +2181,7 @@
                     { watchState, listener ->
                         CanvasComplicationDrawable(complicationDrawableLeft, watchState, listener)
                     },
-                    emptyList(),
+                    listOf(ComplicationType.SHORT_TEXT),
                     DefaultComplicationDataSourcePolicy(
                         dataSource1,
                         dataSource2,
diff --git a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
index 9ec6aa3..46ae397 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
@@ -46,6 +46,7 @@
 import org.junit.Assert.assertTrue
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatcher
@@ -155,6 +156,7 @@
         }
     }
 
+    @Ignore // b/277591676
     @Test
     public fun testWindowLayoutUpdatesOnRecreate() {
         assumeExtensionV10_V01()
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
index 80e0f0a..ffce534 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
@@ -52,7 +52,7 @@
         builder.setLastEnqueueTime(now, TimeUnit.MILLISECONDS)
         builder.setInitialDelay(delay, TimeUnit.MILLISECONDS)
         val request = builder.build()
-        mDelayedWorkTracker.schedule(request.workSpec)
+        mDelayedWorkTracker.schedule(request.workSpec, now + delay)
 
         verify(mRunnableScheduler).scheduleWithDelay(
             anyLong(),
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
index 393685e..4783799 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
@@ -16,10 +16,13 @@
 
 package androidx.work.impl.background.greedy;
 
+import static androidx.work.WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
 import static androidx.work.impl.model.WorkSpecKt.generationalId;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -115,7 +118,7 @@
                 .setInitialDelay(1000L, TimeUnit.MILLISECONDS)
                 .build();
         mGreedyScheduler.schedule(work.getWorkSpec());
-        verify(mDelayedWorkTracker).schedule(work.getWorkSpec());
+        verify(mDelayedWorkTracker).schedule(eq(work.getWorkSpec()), anyLong());
     }
 
     @Test
@@ -126,7 +129,7 @@
                 .setInitialRunAttemptCount(5)
                 .build();
         mGreedyScheduler.schedule(work.getWorkSpec());
-        verify(mDelayedWorkTracker).schedule(work.getWorkSpec());
+        verify(mDelayedWorkTracker).schedule(eq(work.getWorkSpec()), anyLong());
     }
 
     @Test
@@ -215,4 +218,24 @@
         verify(mMockProcessor, times(0)).addExecutionListener(mGreedyScheduler);
         verify(mMockWorkConstraintsTracker, never()).replace(ArgumentMatchers.<WorkSpec>anyList());
     }
+
+    @Test
+    @SmallTest
+    public void testGreedyScheduler_throttleWork() {
+        long before = System.currentTimeMillis();
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .setLastEnqueueTime(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
+                .setInitialDelay(1000L, TimeUnit.MILLISECONDS)
+                .build();
+        WorkSpec workSpec = work.getWorkSpec();
+        mGreedyScheduler.schedule(workSpec);
+        mGreedyScheduler.onExecuted(generationalId(workSpec), true);
+        WorkSpec updatedRunAttemptCount = new WorkSpec(workSpec.id, workSpec);
+        updatedRunAttemptCount.runAttemptCount = 10;
+        reset(mDelayedWorkTracker);
+        mGreedyScheduler.schedule(updatedRunAttemptCount);
+        ArgumentCaptor<Long> delayCapture = ArgumentCaptor.forClass(Long.class);
+        verify(mDelayedWorkTracker).schedule(eq(updatedRunAttemptCount), delayCapture.capture());
+        assertThat(delayCapture.getValue()).isAtLeast(before + 5 * DEFAULT_BACKOFF_DELAY_MILLIS);
+    }
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
index 523da2c..01e484a 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
@@ -60,8 +60,9 @@
      * the {@link WorkSpec}'s scheduled run time.
      *
      * @param workSpec The {@link WorkSpec} corresponding to the {@link androidx.work.WorkRequest}
+     * @param nextRunTime time when work should be executed
      */
-    public void schedule(@NonNull final WorkSpec workSpec) {
+    public void schedule(@NonNull final WorkSpec workSpec, long nextRunTime) {
         Runnable existing = mRunnables.remove(workSpec.id);
         if (existing != null) {
             mRunnableScheduler.cancel(existing);
@@ -77,7 +78,7 @@
 
         mRunnables.put(workSpec.id, runnable);
         long now = System.currentTimeMillis();
-        long delay = workSpec.calculateNextRunTime() - now;
+        long delay = nextRunTime - now;
         mRunnableScheduler.scheduleWithDelay(delay, runnable);
     }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
index 61dc2bb..b7e8767 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
@@ -18,8 +18,11 @@
 
 import static android.os.Build.VERSION.SDK_INT;
 
+import static androidx.work.WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
 import static androidx.work.impl.model.WorkSpecKt.generationalId;
 
+import static java.lang.Math.max;
+
 import android.content.Context;
 import android.text.TextUtils;
 
@@ -43,21 +46,28 @@
 import androidx.work.impl.model.WorkSpec;
 import androidx.work.impl.utils.ProcessUtils;
 
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * A greedy {@link Scheduler} that schedules unconstrained, non-timed work.  It intentionally does
  * not acquire any WakeLocks, instead trying to brute-force them as time allows before the process
  * gets killed.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class GreedyScheduler implements Scheduler, WorkConstraintsCallback, ExecutionListener {
 
     private static final String TAG = Logger.tagWithPrefix("GreedyScheduler");
 
+    /**
+     * GreedyScheduler will start throttle workspec if it sees the same work being retried
+     * within process's lifetime.
+     */
+    private static final int NON_THROTTLE_RUN_ATTEMPT_COUNT = 5;
+
     private final Context mContext;
     private final WorkConstraintsTracker mWorkConstraintsTracker;
     private final Set<WorkSpec> mConstrainedWorkSpecs = new HashSet<>();
@@ -69,6 +79,8 @@
     private final WorkLauncher mWorkLauncher;
 
     private final Configuration mConfiguration;
+
+    private final Map<WorkGenerationalId, AttemptData> mFirstRunAttempts = new HashMap<>();
     // Internal State
     Boolean mInDefaultProcess;
 
@@ -138,13 +150,14 @@
             if (mStartStopTokens.contains(id)) {
                 continue;
             }
-            long nextRunTime = workSpec.calculateNextRunTime();
+            long throttled = throttleIfNeeded(workSpec);
+            long nextRunTime = max(workSpec.calculateNextRunTime(), throttled);
             long now = System.currentTimeMillis();
             if (workSpec.state == WorkInfo.State.ENQUEUED) {
                 if (now < nextRunTime) {
                     // Future work
                     if (mDelayedWorkTracker != null) {
-                        mDelayedWorkTracker.schedule(workSpec);
+                        mDelayedWorkTracker.schedule(workSpec, nextRunTime);
                     }
                 } else if (workSpec.hasConstraints()) {
                     if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
@@ -202,7 +215,7 @@
             mDelayedWorkTracker.unschedule(workSpecId);
         }
         // onExecutionCompleted does the cleanup.
-        for (StartStopToken id: mStartStopTokens.remove(workSpecId)) {
+        for (StartStopToken id : mStartStopTokens.remove(workSpecId)) {
             mWorkLauncher.stopWork(id);
         }
     }
@@ -235,6 +248,13 @@
     public void onExecuted(@NonNull WorkGenerationalId id, boolean needsReschedule) {
         mStartStopTokens.remove(id);
         removeConstraintTrackingFor(id);
+
+        if (!needsReschedule) {
+            // finished execution rather than being interrupted
+            synchronized (mLock) {
+                mFirstRunAttempts.remove(id);
+            }
+        }
         // onExecuted does not need to worry about unscheduling WorkSpecs with the mDelayedTracker.
         // This is because, after onExecuted(), all schedulers are asked to cancel.
     }
@@ -263,4 +283,29 @@
             mRegisteredExecutionListener = true;
         }
     }
+
+    private long throttleIfNeeded(WorkSpec workSpec) {
+        synchronized (mLock) {
+            WorkGenerationalId id = generationalId(workSpec);
+            AttemptData firstRunAttempt = mFirstRunAttempts.get(id);
+            if (firstRunAttempt == null) {
+                firstRunAttempt = new AttemptData(workSpec.runAttemptCount,
+                        System.currentTimeMillis());
+                mFirstRunAttempts.put(id, firstRunAttempt);
+            }
+            return firstRunAttempt.mTimeStamp
+                    + max(workSpec.runAttemptCount - firstRunAttempt.mRunAttemptCount
+                    - NON_THROTTLE_RUN_ATTEMPT_COUNT, 0) * DEFAULT_BACKOFF_DELAY_MILLIS;
+        }
+    }
+
+    private static class AttemptData {
+        final int mRunAttemptCount;
+        final long mTimeStamp;
+
+        private AttemptData(int runAttemptCount, long timeStamp) {
+            this.mRunAttemptCount = runAttemptCount;
+            this.mTimeStamp = timeStamp;
+        }
+    }
 }