Merge "[Material3][List] Add experimental API tag to default and color objects." into androidx-main
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 81f3292..6c342ce 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -18,7 +18,7 @@
 
     // Required to make activity 1.5.0-rc01 dependencies resolve.
     implementation("androidx.core:core-ktx:1.8.0")
-    implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.0")
+    implementation(libs.kotlinStdlib)
 
     implementation(projectOrArtifact(":emoji2:emoji2"))
     implementation(projectOrArtifact(":emoji2:emoji2-views-helper"))
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
index b8c582d..07064be 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
@@ -118,7 +118,9 @@
 
     private Resources getResourcesInternal() {
         if (mResources == null) {
-            if (isEmptyConfiguration(mOverrideConfiguration)) {
+            if (mOverrideConfiguration == null
+                    || (Build.VERSION.SDK_INT >= 26
+                    && isEmptyConfiguration(mOverrideConfiguration))) {
                 // If we're not applying any overrides, use the base context's resources. On API
                 // 26+, this will avoid pulling in resources that share a backing implementation
                 // with the application context.
@@ -215,6 +217,7 @@
      * @return {@code true} if the specified configuration is {@code null} or is a no-op when
      *         used as a configuration overlay
      */
+    @RequiresApi(26)
     private static boolean isEmptyConfiguration(Configuration overrideConfiguration) {
         if (overrideConfiguration == null) {
             return true;
diff --git a/bluetooth/bluetooth/build.gradle b/bluetooth/bluetooth/build.gradle
index 9bf971b..a83aefa 100644
--- a/bluetooth/bluetooth/build.gradle
+++ b/bluetooth/bluetooth/build.gradle
@@ -24,6 +24,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
+    implementation 'androidx.annotation:annotation:1.4.0'
 }
 
 androidx {
@@ -36,4 +37,7 @@
 
 android {
     namespace "androidx.bluetooth"
+    defaultConfig {
+        minSdkVersion 21
+    }
 }
diff --git a/bluetooth/bluetooth/src/main/AndroidManifest.xml b/bluetooth/bluetooth/src/main/AndroidManifest.xml
index 6abff90..24c3903 100644
--- a/bluetooth/bluetooth/src/main/AndroidManifest.xml
+++ b/bluetooth/bluetooth/src/main/AndroidManifest.xml
@@ -13,4 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
\ No newline at end of file
+
+
+    
+
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
new file mode 100644
index 0000000..2aaf3a5
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.bluetooth
+
+import android.os.Build
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.bluetooth.utils.Bundleable
+
+import java.util.UUID
+
+/**
+ * @hide
+ */
+class BluetoothGattDescriptor internal constructor(
+    descriptor: android.bluetooth.BluetoothGattDescriptor
+) : Bundleable {
+    private val impl: GattDescriptorImpl =
+        if (Build.VERSION.SDK_INT >= 24) {
+            GattDescriptorImplApi24(descriptor)
+        } else {
+            GattDescriptorImplApi21(descriptor)
+        }
+    internal val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor
+        get() = impl.fwkDescriptor
+    val permissions: Int
+        get() = impl.permissions
+    val uuid: UUID
+        get() = impl.uuid
+    val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+        get() = impl.characteristic
+
+    constructor(uuid: UUID, permissions: Int) : this(
+        android.bluetooth.BluetoothGattDescriptor(
+            uuid,
+            permissions
+        )
+    )
+
+    companion object {
+        const val PERMISSION_READ = android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ
+        const val PERMISSION_READ_ENCRYPTED =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED
+        const val PERMISSION_READ_ENCRYPTED_MITM =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED_MITM
+        const val PERMISSION_WRITE = android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE
+        const val PERMISSION_WRITE_ENCRYPTED =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED
+        const val PERMISSION_WRITE_ENCRYPTED_MITM =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED_MITM
+        const val PERMISSION_WRITE_SIGNED =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED
+        const val PERMISSION_WRITE_SIGNED_MITM =
+            android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED_MITM
+
+        val ENABLE_NOTIFICATION_VALUE = byteArrayOf(0x01, 0x00)
+        val ENABLE_INDICATION_VALUE = byteArrayOf(0x02, 0x00)
+        val DISABLE_NOTIFICATION_VALUE = byteArrayOf(0x00, 0x00)
+
+        internal fun keyForField(field: Int): String? {
+            return field.toString(Character.MAX_RADIX)
+        }
+
+        val CREATOR: Bundleable.Creator =
+            if (Build.VERSION.SDK_INT >= 24) GattDescriptorImplApi24.CREATOR
+            else GattDescriptorImplApi21.CREATOR
+    }
+
+    override fun toBundle(): Bundle {
+        return impl.toBundle()
+    }
+
+    private interface GattDescriptorImpl {
+        val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor
+        val permissions: Int
+        val uuid: UUID
+        val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+        fun toBundle(): Bundle
+    }
+
+    private open class GattDescriptorImplApi21(
+        descriptor: android.bluetooth.BluetoothGattDescriptor
+    ) : GattDescriptorImpl {
+        companion object {
+
+            internal const val FIELD_FWK_DESCRIPTOR_PERMISSIONS = 1
+            internal const val FIELD_FWK_DESCRIPTOR_UUID = 2
+            internal const val FIELD_FWK_DESCRIPTOR_INSTANCE = 3
+
+            val CREATOR: Bundleable.Creator =
+                object : Bundleable.Creator {
+
+                    @Suppress("DEPRECATION")
+                    override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
+                        val permissions =
+                            bundle.getInt(
+                                keyForField(FIELD_FWK_DESCRIPTOR_PERMISSIONS),
+                                -1
+                            )
+                        val uuid = bundle.getString(
+                            keyForField(FIELD_FWK_DESCRIPTOR_UUID),
+                        ) ?: throw IllegalArgumentException("Bundle doesn't include uuid")
+
+                        if (permissions == -1) {
+                            throw IllegalArgumentException("Bundle doesn't include permission")
+                        }
+
+                        val descriptor =
+                            android.bluetooth.BluetoothGattDescriptor(
+                                UUID.fromString(uuid),
+                                permissions
+                            )
+
+                        descriptor.javaClass.getDeclaredField("mInstance").setInt(
+                            descriptor, bundle.getInt(
+                                keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), 0
+                            )
+                        )
+                        return BluetoothGattDescriptor(descriptor)
+                    }
+                }
+        }
+
+        override val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor = descriptor
+        override val permissions: Int
+            get() = fwkDescriptor.permissions
+        override val uuid: UUID
+            get() = fwkDescriptor.uuid
+        override val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+            get() = fwkDescriptor.characteristic
+
+        override fun toBundle(): Bundle {
+            val bundle = Bundle()
+            bundle.putString(keyForField(FIELD_FWK_DESCRIPTOR_UUID), uuid.toString())
+            bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_PERMISSIONS), permissions)
+            val instanceId: Int =
+                fwkDescriptor.javaClass.getDeclaredField("mInstance").getInt(fwkDescriptor)
+            bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), instanceId)
+            return bundle
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.N)
+    private open class GattDescriptorImplApi24(
+        descriptor: android.bluetooth.BluetoothGattDescriptor
+    ) : GattDescriptorImplApi21(descriptor) {
+        companion object {
+            internal const val FIELD_FWK_DESCRIPTOR = 0
+            val CREATOR: Bundleable.Creator =
+                object : Bundleable.Creator {
+                    @Suppress("DEPRECATION")
+                    override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
+                        val fwkDescriptor =
+                            bundle.getParcelable(
+                                keyForField(FIELD_FWK_DESCRIPTOR)
+                            ) ?: throw IllegalArgumentException("Bundle doesn't contain descriptor")
+                        return BluetoothGattDescriptor(fwkDescriptor)
+                    }
+                }
+        }
+
+        override fun toBundle(): Bundle {
+            val bundle = Bundle()
+            bundle.putParcelable(
+                keyForField(FIELD_FWK_DESCRIPTOR),
+                fwkDescriptor
+            )
+            return bundle
+        }
+    }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt
new file mode 100644
index 0000000..73440dd
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.bluetooth.utils
+
+import android.os.Bundle
+
+/**
+ * @hide
+ */
+interface Bundleable {
+    /** Returns a [Bundle] representing the information stored in this object.  */
+    fun toBundle(): Bundle
+
+    /** Interface for the static `CREATOR` field of [Bundleable] classes.  */
+    interface Creator {
+        /**
+         * Restores a [Bundleable] instance from a [Bundle] produced by [ ][Bundleable.toBundle].
+         *
+         *
+         * It guarantees the compatibility of [Bundle] representations produced by different
+         * versions of [Bundleable.toBundle] by providing best default values for missing
+         * fields. It throws an exception if any essential fields are missing.
+         */
+        fun fromBundle(bundle: Bundle): T
+    }
+}
\ No newline at end of file
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt b/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
index 94dd541..35c08d3 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
@@ -165,16 +165,21 @@
     fun AndroidXSelfTestProject.readPublishedFile(fileName: String) =
         mavenLocalDir.resolve("$groupId/$artifactId/$version/$fileName").readText()
 
+    var printBuildFileOnFailure: Boolean = false
+
     override fun toString(): String {
         return buildMap {
             put("root files", setup.rootDir.list().orEmpty().toList())
-            setup.rootDir.listFiles().orEmpty().filter { it.isDirectory }.forEach { maybeGroupDir ->
-                maybeGroupDir.listFiles().orEmpty().filter { it.isDirectory }.forEach {
-                    val maybeBuildFile = it.resolve("build.gradle")
-                    if (maybeBuildFile.exists()) {
-                        put(it.name + "/build.gradle", maybeBuildFile.readText())
+            if (printBuildFileOnFailure) {
+                setup.rootDir.listFiles().orEmpty().filter { it.isDirectory }
+                    .forEach { maybeGroupDir ->
+                        maybeGroupDir.listFiles().orEmpty().filter { it.isDirectory }.forEach {
+                            val maybeBuildFile = it.resolve("build.gradle")
+                            if (maybeBuildFile.exists()) {
+                                put(it.name + "/build.gradle", maybeBuildFile.readText())
+                            }
+                        }
                     }
-                }
             }
         }.toString()
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 2a58f28d..89a23c4 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -51,7 +51,6 @@
 import com.android.build.gradle.TestExtension
 import com.android.build.gradle.TestPlugin
 import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.lint.AndroidLintTask
 import com.android.build.gradle.internal.tasks.AnalyticsRecordingTask
 import com.android.build.gradle.internal.tasks.ListingFileRedirectTask
 import org.gradle.api.GradleException
@@ -383,17 +382,6 @@
             }
         }
 
-        // Remove the lint and column attributes from generated lint baseline XML.
-        project.tasks.withType(AndroidLintTask::class.java).configureEach { task ->
-            if (task.name.startsWith("updateLintBaseline")) {
-                task.doLast {
-                    task.outputs.files.find { it.name == "lint-baseline.xml" }?.let { file ->
-                        file.writeText(removeLineAndColumnAttributes(file.readText()))
-                    }
-                }
-            }
-        }
-
         // Remove the android:targetSdkVersion element from the manifest used for AARs.
         project.extensions.getByType().onVariants { variant ->
             project.tasks.register(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index bc5938e..fb481768 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -95,17 +95,19 @@
         } else { null }
     }
 
+    /**
+     * Configures all ios targets supported by AndroidX.
+     */
     @JvmOverloads
-    fun iosArm32(
+    fun ios(
         block: Action? = null
-    ): KotlinNativeTarget? {
-        return if (project.enableMac()) {
-            kotlinExtension.iosArm32().also {
-                block?.execute(it)
-            }
-        } else { null }
+    ): List {
+        return listOfNotNull(
+            iosX64(block),
+            iosArm64(block),
+            iosSimulatorArm64(block)
+        )
     }
-
     @JvmOverloads
     fun iosX64(
         block: Action? = null
@@ -118,6 +120,17 @@
     }
 
     @JvmOverloads
+    fun iosSimulatorArm64(
+        block: Action? = null
+    ): KotlinNativeTarget? {
+        return if (project.enableMac()) {
+            kotlinExtension.iosSimulatorArm64().also {
+                block?.execute(it)
+            }
+        } else { null }
+    }
+
+    @JvmOverloads
     fun linuxX64(
         block: Action? = null
     ): KotlinNativeTargetWithHostTests? {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index fb9439f..fe5eb3d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -41,7 +41,6 @@
 import org.gradle.api.tasks.bundling.ZipEntryCompression
 import org.gradle.build.event.BuildEventsListenerRegistry
 import org.gradle.kotlin.dsl.extra
-import org.jetbrains.kotlin.gradle.plugin.sources.CleanupStaleSourceSetMetadataEntriesService
 
 abstract class AndroidXRootImplPlugin : Plugin {
     @Suppress("UnstableApiUsage")
@@ -58,7 +57,6 @@
     private fun Project.configureRootProject() {
         project.validateAllAndroidxArgumentsAreRecognized()
         tasks.register("listAndroidXProperties", ListAndroidXPropertiesTask::class.java)
-        this.disableKmpKlibCleanupService()
         setDependencyVersions()
         configureKtlintCheckFile()
         tasks.register(CheckExternalDependencyLicensesTask.TASK_NAME)
@@ -240,35 +238,4 @@
         androidx.build.dependencies.agpVersion = getVersionByName("androidGradlePlugin")
         androidx.build.dependencies.guavaVersion = getVersionByName("guavaJre")
     }
-
-    /**
-     * This function applies the workaround for b/236850628.
-     * There is a BuildService in KMP Gradle Plugin 1.7.0 that tries to cleanup unused klibs,
-     * which is over-eager and causes missing dependencies in the Studio UI. This function simply
-     * breaks the inputs of that cleanup function by intercepting the service creation and changing
-     * its parameters.
-     *
-     * That code is already removed in KMP main branch and we should be able to remove this
-     * workaround after updating to 1.7.20.
-     *
-     * see b/236850628#comment25 for more details.
-     */
-    private fun Project.disableKmpKlibCleanupService() {
-        project.gradle.sharedServices.registrations.whenObjectAdded { serviceRegistration ->
-            if (serviceRegistration.name == "cleanup-stale-sourceset-metadata") {
-                val params = serviceRegistration.parameters as
-                    CleanupStaleSourceSetMetadataEntriesService.Parameters
-                val tmpDirectory = project.layout.buildDirectory.dir("klib-cleanup-workaround")
-                params.projectStorageRoot.set(
-                    tmpDirectory.map { it.asFile }
-                )
-                params.projectStorageDirectories.set(emptyMap())
-                // make sure the store root is not changed afterwards. we don't need to set this
-                // for projectStorageDirectories as the files are deleted from projectStorageRoot,
-                // not projectStorageDirectories.
-                // https://github.com/JetBrains/kotlin/blob/47beef16f38ea315bf4d7d4d3faf34b0b952b613/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt#L26
-                params.projectStorageRoot.disallowChanges()
-            }
-        }
-    }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 2c76d84..288c32b8 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -19,6 +19,7 @@
 import androidx.build.dependencyTracker.AffectedModuleDetector
 import com.android.build.api.dsl.Lint
 import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
+import com.android.build.gradle.internal.lint.AndroidLintTask
 import java.io.File
 import java.util.Locale
 import org.gradle.api.GradleException
@@ -171,6 +172,17 @@
         )
     }
 
+    tasks.withType(AndroidLintTask::class.java).configureEach { task ->
+        // Remove the lint and column attributes from generated lint baseline XML.
+        if (task.name.startsWith("updateLintBaseline")) {
+            task.doLast {
+                task.outputs.files.find { it.name == "lint-baseline.xml" }?.let { file ->
+                    file.writeText(removeLineAndColumnAttributes(file.readText()))
+                }
+            }
+        }
+    }
+
     // Lint is configured entirely in finalizeDsl so that individual projects cannot easily
     // disable individual checks in the DSL for any reason.
     lint.apply {
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt b/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
index 5f2c434..0b79b6d 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
@@ -25,6 +25,7 @@
     var js = Publish.UNSET
     var mac = Publish.UNSET
     var linux = Publish.UNSET
+    var ios = Publish.UNSET
 
     /**
      * List of platforms names which should be published to maven. e.g. ["jvm", "js"]
@@ -44,10 +45,13 @@
             if (linux.shouldPublish()) {
                 platforms.addAll(linuxPlatforms)
             }
+            if (ios.shouldPublish()) {
+                platforms.addAll(iosPlatforms)
+            }
             return platforms
         }
     private val allExtendedPlatforms
-        get() = listOf(jvm, js, mac, linux)
+        get() = listOf(jvm, js, mac, linux, ios)
     private val allPlatforms
         get() = listOf(android) + allExtendedPlatforms
     private val activeExtendedPlatforms
@@ -73,7 +77,11 @@
         private const val MAC_ARM_64 = "macosarm64"
         private const val MAC_OSX_64 = "macosx64"
         private const val LINUX_64 = "linuxx64"
+        private const val IOS_SIMULATOR_ARM_64 = "iossimulatorarm64"
+        private const val IOS_X_64 = "iosx64"
+        private const val IOS_ARM_64 = "iosarm64"
         private val macPlatforms = listOf(MAC_ARM_64, MAC_OSX_64)
         private val linuxPlatforms = listOf(LINUX_64)
+        private val iosPlatforms = listOf(IOS_SIMULATOR_ARM_64, IOS_ARM_64, IOS_X_64)
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
index a524d23..5f74052 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
@@ -45,11 +45,16 @@
  * [android.hardware.camera2.CameraDevice] and [android.hardware.camera2.CameraCaptureSession] via
  * the [CameraGraph] interface.
  */
-public class CameraPipe(config: Config, threadConfig: ThreadConfig = ThreadConfig()) {
+public class CameraPipe(config: Config) {
+
+    @Deprecated("threadConfig should be specified on config.")
+    @Suppress("UNUSED_PARAMETER")
+    public constructor(config: Config, threadConfig: ThreadConfig) : this(config)
+
     private val debugId = cameraPipeIds.incrementAndGet()
     private val component: CameraPipeComponent = DaggerCameraPipeComponent.builder()
         .cameraPipeConfigModule(CameraPipeConfigModule(config))
-        .threadConfigModule(ThreadConfigModule(threadConfig))
+        .threadConfigModule(ThreadConfigModule(config.threadConfig))
         .build()
 
     /**
diff --git a/car/app/app-automotive/api/current.txt b/car/app/app-automotive/api/current.txt
index fdfc2bc..e0b9e41 100644
--- a/car/app/app-automotive/api/current.txt
+++ b/car/app/app-automotive/api/current.txt
@@ -3,7 +3,7 @@
 
   public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
     ctor public BaseCarAppActivity();
-    method public void bindToViewModel();
+    method public void bindToViewModel(androidx.car.app.SessionInfo);
     method public android.content.ComponentName? retrieveServiceComponentName();
   }
 
diff --git a/car/app/app-automotive/api/public_plus_experimental_current.txt b/car/app/app-automotive/api/public_plus_experimental_current.txt
index 83f0862..d7ebfbc 100644
--- a/car/app/app-automotive/api/public_plus_experimental_current.txt
+++ b/car/app/app-automotive/api/public_plus_experimental_current.txt
@@ -3,7 +3,7 @@
 
   public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
     ctor public BaseCarAppActivity();
-    method public void bindToViewModel();
+    method public void bindToViewModel(androidx.car.app.SessionInfo);
     method public android.content.ComponentName? retrieveServiceComponentName();
   }
 
diff --git a/car/app/app-automotive/api/restricted_current.txt b/car/app/app-automotive/api/restricted_current.txt
index cf8af8b..9c3b7b0 100644
--- a/car/app/app-automotive/api/restricted_current.txt
+++ b/car/app/app-automotive/api/restricted_current.txt
@@ -3,7 +3,7 @@
 
   public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity {
     ctor public BaseCarAppActivity();
-    method public void bindToViewModel();
+    method public void bindToViewModel(androidx.car.app.SessionInfo);
     method public android.content.ComponentName? retrieveServiceComponentName();
   }
 
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
index 9c0574f..13b2553 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
@@ -40,6 +40,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IInsetsListener;
 import androidx.car.app.activity.renderer.IRendererCallback;
@@ -252,7 +253,7 @@
     /**
      * Binds the {@link BaseCarAppActivity} and it's view against the view model.
      */
-    public void bindToViewModel() {
+    public void bindToViewModel(@NonNull SessionInfo sessionInfo) {
         ComponentName serviceComponentName = retrieveServiceComponentName();
         if (serviceComponentName == null) {
             Log.e(LogTags.TAG, "Unspecified service class name");
@@ -261,7 +262,7 @@
         }
 
         CarAppViewModelFactory factory = CarAppViewModelFactory.getInstance(getApplication(),
-                serviceComponentName);
+                serviceComponentName, sessionInfo);
         mViewModel = new ViewModelProvider(this, factory).get(CarAppViewModel.class);
         mViewModel.setActivity(this);
         mViewModel.resetState();
@@ -329,9 +330,7 @@
     }
 
     private void onErrorChanged(@Nullable ErrorHandler.ErrorType errorType) {
-        ThreadUtils.runOnMain(() -> {
-            mErrorMessageView.setError(errorType);
-        });
+        ThreadUtils.runOnMain(() -> mErrorMessageView.setError(errorType));
     }
 
     private void onStateChanged(@NonNull CarAppViewModel.State state) {
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
index faf5f42..c5d96a5 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
@@ -16,6 +16,10 @@
 
 package androidx.car.app.activity;
 
+import static androidx.car.app.SessionInfo.DISPLAY_TYPE_MAIN;
+
+import static java.lang.System.identityHashCode;
+
 import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.os.Bundle;
@@ -23,6 +27,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.CarAppService;
+import androidx.car.app.SessionInfo;
 
 /**
  * The class representing a car app activity in the main display.
@@ -76,6 +81,15 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        bindToViewModel();
+
+        String identifier;
+        if (getIntent().getIdentifier() != null) {
+            identifier = getIntent().getIdentifier();
+        } else {
+            identifier = String.valueOf(identityHashCode(this));
+        }
+
+        bindToViewModel(new SessionInfo(DISPLAY_TYPE_MAIN,
+                identifier));
     }
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
index 9b64f77..a1737df 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
@@ -30,6 +30,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IInsetsListener;
 import androidx.car.app.activity.renderer.IRendererCallback;
@@ -56,9 +57,12 @@
     private final MutableLiveData mError = new MutableLiveData<>();
     private final MutableLiveData mState = new MutableLiveData<>(State.IDLE);
     private ServiceConnectionManager mServiceConnectionManager;
-    @Nullable private IRendererCallback mIRendererCallback;
-    @Nullable private IInsetsListener mIInsetsListener;
-    @Nullable private Insets mInsets = Insets.NONE;
+    @Nullable
+    private IRendererCallback mIRendererCallback;
+    @Nullable
+    private IInsetsListener mIInsetsListener;
+    @Nullable
+    private Insets mInsets = Insets.NONE;
     private static WeakReference sActivity = new WeakReference<>(null);
 
     /** Possible view states */
@@ -73,14 +77,17 @@
         ERROR,
     }
 
-    public CarAppViewModel(@NonNull Application application, @NonNull ComponentName componentName) {
+    public CarAppViewModel(@NonNull Application application,
+            @NonNull ComponentName componentName, @NonNull SessionInfo sessionInfo) {
         super(application);
 
-        mServiceConnectionManager = new ServiceConnectionManager(application, componentName, this);
+        mServiceConnectionManager = new ServiceConnectionManager(application, componentName,
+                sessionInfo, this);
     }
 
     @VisibleForTesting
-    @NonNull ServiceConnectionManager getServiceConnectionManager() {
+    @NonNull
+    ServiceConnectionManager getServiceConnectionManager() {
         return mServiceConnectionManager;
     }
 
@@ -89,7 +96,8 @@
         mServiceConnectionManager = serviceConnectionManager;
     }
 
-    @NonNull ServiceDispatcher getServiceDispatcher() {
+    @NonNull
+    ServiceDispatcher getServiceDispatcher() {
         return mServiceConnectionManager.getServiceDispatcher();
     }
 
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
index 3c9627d..4ce892c 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.car.app.SessionInfo;
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
@@ -40,11 +41,13 @@
 
     Application mApplication;
     ComponentName mComponentName;
+    SessionInfo mSessionInfo;
 
     private CarAppViewModelFactory(@NonNull ComponentName componentName,
-            @NonNull Application application) {
+            @NonNull Application application, @NonNull SessionInfo sessionInfo) {
         mComponentName = componentName;
         mApplication = application;
+        mSessionInfo = sessionInfo;
     }
 
     /**
@@ -54,10 +57,10 @@
      */
     @NonNull
     static CarAppViewModelFactory getInstance(Application application,
-            ComponentName componentName) {
+            ComponentName componentName, SessionInfo sessionInfo) {
         CarAppViewModelFactory instance = sInstances.get(componentName);
         if (instance == null) {
-            instance = new CarAppViewModelFactory(componentName, application);
+            instance = new CarAppViewModelFactory(componentName, application, sessionInfo);
             sInstances.put(componentName, instance);
         }
         return instance;
@@ -67,6 +70,6 @@
     @NonNull
     @Override
     public  T create(@NonNull Class modelClass) {
-        return (T) new CarAppViewModel(mApplication, mComponentName);
+        return (T) new CarAppViewModel(mApplication, mComponentName, mSessionInfo);
     }
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
index 8d91a334..5d8ff94 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
@@ -36,6 +36,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.HandshakeInfo;
+import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IRendererService;
 import androidx.car.app.versioning.CarAppApiLevels;
@@ -58,6 +59,7 @@
 
     final ServiceConnectionListener mListener;
     private final ComponentName mServiceComponentName;
+    private final SessionInfo mSessionInfo;
     private final Context mContext;
     private final ServiceDispatcher mServiceDispatcher;
     private int mDisplayId;
@@ -79,8 +81,10 @@
 
     public ServiceConnectionManager(@NonNull Context context,
             @NonNull ComponentName serviceComponentName,
+            @NonNull SessionInfo sessionInfo,
             @NonNull ServiceConnectionListener listener) {
         mContext = context;
+        mSessionInfo = sessionInfo;
         mListener = listener;
         mServiceComponentName = serviceComponentName;
         mServiceDispatcher = new ServiceDispatcher(listener, this::isBound);
@@ -197,6 +201,7 @@
         }
 
         Intent rendererIntent = new Intent(ACTION_RENDER);
+        SessionInfo.setBindData(rendererIntent, mSessionInfo);
         List resolveInfoList =
                 mContext.getPackageManager()
                         .queryIntentServices(rendererIntent, PackageManager.GET_META_DATA);
@@ -284,7 +289,7 @@
     private boolean updateIntent() {
         ComponentName serviceComponentName = requireNonNull(mServiceComponentName);
         Intent intent = requireNonNull(mIntent);
-
+        SessionInfo.setBindData(intent, mSessionInfo);
         IRendererService service = mRendererService;
         if (service == null) {
             Log.e(LogTags.TAG, "Service dispatcher is not connected");
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
index dd0c09e..b7a7fc9 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
@@ -19,6 +19,8 @@
 import static android.view.KeyEvent.ACTION_UP;
 import static android.view.KeyEvent.KEYCODE_R;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
@@ -51,6 +53,7 @@
 import android.view.inputmethod.InputConnection;
 
 import androidx.car.app.CarAppService;
+import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IInsetsListener;
 import androidx.car.app.activity.renderer.IProxyInputConnection;
@@ -64,7 +67,6 @@
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
-import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -79,19 +81,20 @@
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 public class CarAppActivityTest {
-    private final ComponentName mRendererComponent = new ComponentName(
-            ApplicationProvider.getApplicationContext(), getClass().getName());
+    public static final String INTENT_IDENTIFIER = "CarAppActivityTest";
+    private final ComponentName mRendererComponent = new ComponentName(getApplicationContext(),
+            getClass().getName());
     private final String mFakeCarAppServiceClass = "com.fake.FakeCarAppService";
     private final ComponentName mFakeCarAppServiceComponent = new ComponentName(
-            ApplicationProvider.getApplicationContext(), mFakeCarAppServiceClass);
+            getApplicationContext(), mFakeCarAppServiceClass);
     private final IRendererService mRenderService = mock(IRendererService.class);
-    private final RenderServiceDelegate mRenderServiceDelegate =
-            new RenderServiceDelegate(mRenderService);
+    private final RenderServiceDelegate mRenderServiceDelegate = new RenderServiceDelegate(
+            mRenderService);
 
     @Before
     public void setup() {
         try {
-            Application app = ApplicationProvider.getApplicationContext();
+            Application app = getApplicationContext();
 
             // Register fake {@code CarAppService}
             PackageManager packageManager = app.getPackageManager();
@@ -105,8 +108,7 @@
             spm.addIntentFilterForService(mRendererComponent,
                     new IntentFilter(CarAppActivity.ACTION_RENDER));
 
-            when(mRenderService.initialize(any(ICarAppActivity.class),
-                    any(ComponentName.class),
+            when(mRenderService.initialize(any(ICarAppActivity.class), any(ComponentName.class),
                     anyInt())).thenReturn(true);
             when(mRenderService.onNewIntent(any(Intent.class), any(ComponentName.class),
                     anyInt())).thenReturn(true);
@@ -122,8 +124,7 @@
     @Test
     public void testRendererInitialization() {
         runOnActivity((scenario, activity) -> {
-            verify(mRenderService, times(1)).initialize(
-                    mRenderServiceDelegate.getCarAppActivity(),
+            verify(mRenderService, times(1)).initialize(mRenderServiceDelegate.getCarAppActivity(),
                     mFakeCarAppServiceComponent, activity.getDisplayId());
             verify(mRenderService, times(1)).onNewIntent(activity.getIntent(),
                     mFakeCarAppServiceComponent, activity.getDisplayId());
@@ -162,12 +163,10 @@
             verify(callback, times(1)).onResume();
 
             // Add a test-specific lifecycle callback to activity.
-            ActivityLifecycleCallbacks activityCallback = mock(
-                    ActivityLifecycleCallbacks.class);
+            ActivityLifecycleCallbacks activityCallback = mock(ActivityLifecycleCallbacks.class);
             activity.registerActivityLifecycleCallbacks(activityCallback);
             // Report service connection error.
-            CarAppViewModel viewModel =
-                    new ViewModelProvider(activity).get(CarAppViewModel.class);
+            CarAppViewModel viewModel = new ViewModelProvider(activity).get(CarAppViewModel.class);
             viewModel.onError(ErrorHandler.ErrorType.HOST_ERROR);
 
             assertThat(activity.isFinishing()).isEqualTo(false);
@@ -197,19 +196,17 @@
         runOnActivity((scenario, activity) -> {
             ServiceConnectionManager serviceConnectionManager =
                     activity.mViewModel.getServiceConnectionManager();
-            ServiceConnection serviceConnection =
-                    spy(serviceConnectionManager.getServiceConnection());
+            ServiceConnection serviceConnection = spy(
+                    serviceConnectionManager.getServiceConnection());
             serviceConnectionManager.setServiceConnection(serviceConnection);
 
             // Destroy activity to force unbind.
             scenario.moveToState(Lifecycle.State.DESTROYED);
 
             // Verify Activity onDestroy even is reported to renderer.
-            verify(mRenderService, times(1)).terminate(
-                    mFakeCarAppServiceComponent);
+            verify(mRenderService, times(1)).terminate(mFakeCarAppServiceComponent);
             // Verify service connection is closed.
-            verify(serviceConnection, times(1)).onServiceDisconnected(
-                    mRendererComponent);
+            verify(serviceConnection, times(1)).onServiceDisconnected(mRendererComponent);
             assertThat(serviceConnectionManager.isBound()).isFalse();
 
         });
@@ -223,8 +220,7 @@
             IRendererCallback rendererCallback = mock(IRendererCallback.class);
 
             ICarAppActivity carAppActivity = mRenderServiceDelegate.getCarAppActivity();
-            carAppActivity.setSurfacePackage(
-                    Bundleable.create(new LegacySurfacePackage(callback)));
+            carAppActivity.setSurfacePackage(Bundleable.create(new LegacySurfacePackage(callback)));
             carAppActivity.registerRendererCallback(rendererCallback);
 
             // Verify back events on the activity are sent to host.
@@ -243,11 +239,9 @@
             int x = 50;
             int y = 50;
             int metaState = 0;
-            MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y,
-                    metaState);
+            MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, metaState);
             activity.mSurfaceView.dispatchTouchEvent(event);
-            ArgumentCaptor argument = ArgumentCaptor.forClass(
-                    MotionEvent.class);
+            ArgumentCaptor argument = ArgumentCaptor.forClass(MotionEvent.class);
             verify(callback, times(1)).onTouchEvent(argument.capture());
             // Compare string representations as equals in MotionEvent checks for same
             // object.
@@ -306,15 +300,13 @@
             View activityContainer = activity.mActivityContainerView;
             View localContentContainer = activity.mLocalContentContainerView;
             Insets systemWindowInsets = Insets.of(10, 20, 30, 40);
-            WindowInsets windowInsets = new WindowInsetsCompat.Builder()
-                    .setInsets(WindowInsetsCompat.Type.systemBars(), systemWindowInsets)
-                    .build()
-                    .toWindowInsets();
+            WindowInsets windowInsets = new WindowInsetsCompat.Builder().setInsets(
+                    WindowInsetsCompat.Type.systemBars(),
+                    systemWindowInsets).build().toWindowInsets();
             activityContainer.onApplyWindowInsets(windowInsets);
 
             // Verify that the host is notified and insets are not handled locally
-            verify(insetsListener).onInsetsChanged(
-                    eq(systemWindowInsets.toPlatformInsets()));
+            verify(insetsListener).onInsetsChanged(eq(systemWindowInsets.toPlatformInsets()));
             assertThat(activityContainer.getPaddingBottom()).isEqualTo(0);
             assertThat(activityContainer.getPaddingTop()).isEqualTo(0);
             assertThat(activityContainer.getPaddingLeft()).isEqualTo(0);
@@ -346,6 +338,62 @@
         });
     }
 
+    @Test
+    public void testLaunchWithIdentifier_passesAlongValues() {
+        ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+        newIntent.setIdentifier(INTENT_IDENTIFIER);
+        try (ActivityScenario scenario = ActivityScenario.launch(newIntent)) {
+            scenario.onActivity(activity -> {
+                try {
+                    verify(mRenderService, times(1)).initialize(
+                            mRenderServiceDelegate.getCarAppActivity(), mFakeCarAppServiceComponent,
+                            activity.getDisplayId());
+                    verify(mRenderService, times(1)).onNewIntent(intentArgumentCaptor.capture(),
+                            eq(mFakeCarAppServiceComponent), eq(activity.getDisplayId()));
+
+                    Intent intent = intentArgumentCaptor.getValue();
+                    SessionInfo si = new SessionInfo(intent);
+
+                    assertThat(si.getDisplayType()).isEqualTo(SessionInfo.DISPLAY_TYPE_MAIN);
+                    assertThat(intent.getIdentifier()).isEqualTo(si.toString());
+                } catch (Exception e) {
+                    fail(Log.getStackTraceString(e));
+                }
+            });
+
+        }
+
+    }
+
+    @Test
+    public void testLaunchWithoutIdentifier_setsRandomIdValue() {
+        ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+        try (ActivityScenario scenario = ActivityScenario.launch(newIntent)) {
+            scenario.onActivity(activity -> {
+                try {
+                    verify(mRenderService, times(1)).initialize(
+                            mRenderServiceDelegate.getCarAppActivity(), mFakeCarAppServiceComponent,
+                            activity.getDisplayId());
+                    verify(mRenderService, times(1)).onNewIntent(intentArgumentCaptor.capture(),
+                            eq(mFakeCarAppServiceComponent), eq(activity.getDisplayId()));
+
+                    Intent intent = intentArgumentCaptor.getValue();
+                    SessionInfo si = new SessionInfo(intent);
+
+                    assertThat(si.getDisplayType()).isEqualTo(SessionInfo.DISPLAY_TYPE_MAIN);
+                    assertThat(intent.getIdentifier()).isNotEqualTo(INTENT_IDENTIFIER);
+                    assertThat(intent.getIdentifier()).isEqualTo(si.toString());
+                } catch (Exception e) {
+                    fail(Log.getStackTraceString(e));
+                }
+            });
+
+        }
+
+    }
+
     interface CarActivityAction {
         void accept(ActivityScenario scenario, CarAppActivity activity)
                 throws Exception;
@@ -361,6 +409,7 @@
                     fail(Log.getStackTraceString(e));
                 }
             });
+
         }
     }
 }
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
index 7650a2d..8625a22 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app.activity;
 
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Application;
@@ -42,10 +44,10 @@
     @Test
     public void getInstance_sameKey_returnsSame() {
         CarAppViewModelFactory factory1 = CarAppViewModelFactory.getInstance(mApplication,
-                TEST_COMPONENT_NAME_1);
+                TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
 
         CarAppViewModelFactory factory2 = CarAppViewModelFactory.getInstance(mApplication,
-                TEST_COMPONENT_NAME_1);
+                TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
 
         assertThat(factory1).isEqualTo(factory2);
     }
@@ -53,10 +55,10 @@
     @Test
     public void getInstance_differentKeys_returnsDifferent() {
         CarAppViewModelFactory factory1 = CarAppViewModelFactory.getInstance(mApplication,
-                TEST_COMPONENT_NAME_1);
+                TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
 
         CarAppViewModelFactory factory2 = CarAppViewModelFactory.getInstance(mApplication,
-                TEST_COMPONENT_NAME_2);
+                TEST_COMPONENT_NAME_2, DEFAULT_SESSION_INFO);
 
         assertThat(factory1).isNotEqualTo(factory2);
     }
@@ -64,7 +66,7 @@
     @Test
     public void create_correctComponentName() {
         CarAppViewModelFactory factory = CarAppViewModelFactory.getInstance(mApplication,
-                TEST_COMPONENT_NAME_1);
+                TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
 
         CarAppViewModel viewModel = factory.create(CarAppViewModel.class);
 
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
index 3c7741f3..353977c 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app.activity;
 
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -39,7 +41,7 @@
 
 /** Tests for {@link CarAppViewModel} */
 @RunWith(RobolectricTestRunner.class)
-@Config(instrumentedPackages = { "androidx.car.app.activity" })
+@Config(instrumentedPackages = {"androidx.car.app.activity"})
 @DoNotInstrument
 public class CarAppViewModelTest {
     private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
@@ -49,7 +51,7 @@
 
     private final Application mApplication = ApplicationProvider.getApplicationContext();
     private CarAppViewModel mCarAppViewModel;
-    private CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
+    private final CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
     private ICarAppActivity mICarAppActivity;
     private ShadowLooper mMainLooper;
     private final ServiceConnectionManager mServiceConnectionManager =
@@ -57,7 +59,8 @@
 
     @Before
     public void setUp() {
-        mCarAppViewModel = new CarAppViewModel(mApplication, TEST_COMPONENT_NAME);
+        mCarAppViewModel = new CarAppViewModel(mApplication, TEST_COMPONENT_NAME,
+                DEFAULT_SESSION_INFO);
         mCarAppViewModel.setActivity(mCarAppActivity);
         mICarAppActivity = mock(ICarAppActivity.class);
         mMainLooper = shadowOf(mApplication.getMainLooper());
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
index 6f987f8..ca31699 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app.activity;
 
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -43,19 +45,20 @@
 
 /** Tests for {@link ResultManagerAutomotive}. */
 @RunWith(RobolectricTestRunner.class)
-@Config(instrumentedPackages = { "androidx.car.app.activity" })
+@Config(instrumentedPackages = {"androidx.car.app.activity"})
 @DoNotInstrument
 public class ResultManagerAutomotiveTest {
     private final ComponentName mRendererComponent = new ComponentName(
             ApplicationProvider.getApplicationContext(), getClass().getName());
     private final Application mApplication = ApplicationProvider.getApplicationContext();
-    private CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
-    private ResultManagerAutomotive mResultManager = new ResultManagerAutomotive();
+    private final CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
+    private final ResultManagerAutomotive mResultManager = new ResultManagerAutomotive();
     private CarAppViewModel mCarAppViewModel;
 
     @Before
     public void setUp() {
-        mCarAppViewModel = new CarAppViewModel(mApplication, mRendererComponent);
+        mCarAppViewModel = new CarAppViewModel(mApplication, mRendererComponent,
+                DEFAULT_SESSION_INFO);
     }
 
     @Test
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
index 5bfc022..72c040b 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
@@ -18,11 +18,14 @@
 
 import static android.os.Looper.getMainLooper;
 
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -38,6 +41,7 @@
 import android.util.Log;
 
 import androidx.car.app.HandshakeInfo;
+import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IRendererService;
 import androidx.car.app.serialization.Bundleable;
@@ -47,6 +51,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.internal.DoNotInstrument;
 import org.robolectric.shadows.ShadowApplication;
@@ -72,7 +77,7 @@
             new RenderServiceDelegate(mRenderService);
     private final CarAppViewModel mViewModel =
             new CarAppViewModel(ApplicationProvider.getApplicationContext(),
-                    mFakeCarAppServiceComponent);
+                    mFakeCarAppServiceComponent, DEFAULT_SESSION_INFO);
     private final ServiceConnectionManager mServiceConnectionManager =
             mViewModel.getServiceConnectionManager();
     private final ShadowLooper mMainLooper = shadowOf(getMainLooper());
@@ -125,7 +130,7 @@
     public void testBind_unbound_bindsToRenderer() {
         setupCarAppActivityForTesting();
         ICarAppActivity iCarAppActivity = mock(ICarAppActivity.class);
-
+        ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
         mServiceConnectionManager.bind(TEST_INTENT, iCarAppActivity, TEST_DISPLAY_ID);
         mMainLooper.idle();
         try {
@@ -133,12 +138,17 @@
             assertThat(mViewModel.getError().getValue()).isNull();
             verify(mRenderService).initialize(iCarAppActivity, mFakeCarAppServiceComponent,
                     TEST_DISPLAY_ID);
-            verify(mRenderService).onNewIntent(TEST_INTENT, mFakeCarAppServiceComponent,
-                    TEST_DISPLAY_ID);
+            verify(mRenderService).onNewIntent(intentArgumentCaptor.capture(),
+                    eq(mFakeCarAppServiceComponent),
+                    eq(TEST_DISPLAY_ID));
         } catch (RemoteException e) {
             fail(Log.getStackTraceString(e));
         }
         assertThat(mServiceConnectionManager.isBound()).isTrue();
+        assertThat(intentArgumentCaptor.getValue()).isEqualTo(TEST_INTENT);
+        assertThat(intentArgumentCaptor.getValue().getExtras()).isNotNull();
+        assertThat(new SessionInfo(intentArgumentCaptor.getValue())).isEqualTo(
+                DEFAULT_SESSION_INFO);
     }
 
     @Test
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 347b66d..a696657a 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -159,11 +159,14 @@
 
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
     ctor public SessionInfo(int, String);
+    ctor public SessionInfo(android.content.Intent);
     method public int getDisplayType();
     method public String getSessionId();
+    method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
     field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
     field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
     field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+    field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
   }
 
   public interface SurfaceCallback {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 0556f7b..d69fc33 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -160,12 +160,15 @@
 
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
     ctor public SessionInfo(int, String);
+    ctor public SessionInfo(android.content.Intent);
     method public int getDisplayType();
     method public String getSessionId();
     method @androidx.car.app.annotations.ExperimentalCarApi public java.util.Set!>? getSupportedTemplates(int);
+    method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
     field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
     field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
     field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+    field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
   }
 
   public interface SurfaceCallback {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 347b66d..a696657a 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -159,11 +159,14 @@
 
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
     ctor public SessionInfo(int, String);
+    ctor public SessionInfo(android.content.Intent);
     method public int getDisplayType();
     method public String getSessionId();
+    method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
     field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
     field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
     field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+    field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
   }
 
   public interface SurfaceCallback {
diff --git a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
index 3176923..8006c178 100644
--- a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
@@ -17,15 +17,24 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.car.app.annotations.CarProtocol;
 import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.Template;
 import androidx.car.app.navigation.model.NavigationTemplate;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.versioning.CarAppApiLevel;
 import androidx.car.app.versioning.CarAppApiLevels;
 
@@ -41,6 +50,9 @@
 public class SessionInfo {
     private static final char DIVIDER = '/';
 
+    /** The key for a {@link Bundleable} extra containing the {@link SessionInfo} for a bind. */
+    public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
+
     /** The primary infotainment display usually in the center column of the vehicle. */
     public static final int DISPLAY_TYPE_MAIN = 0;
 
@@ -101,6 +113,53 @@
         mSessionId = sessionId;
     }
 
+    /**
+     * Creates a new {@link SessionInfo} for a given bind {@code intent}
+     */
+    @SuppressWarnings("deprecation")
+    public SessionInfo(@NonNull Intent intent) {
+        Bundle extras = intent.getExtras();
+        if (extras == null) {
+            throw new IllegalArgumentException(
+                    "Expected the SessionInfo to be encoded in the bind intent extras, but the "
+                            + "extras were null.");
+        }
+
+        Bundleable sessionInfoBundleable = extras.getParcelable(EXTRA_SESSION_INFO);
+        if (sessionInfoBundleable == null) {
+            throw new IllegalArgumentException(
+                    "Expected the SessionInfo to be encoded in the bind intent extras, but they "
+                            + "couldn't be found in the extras.");
+        }
+
+        try {
+            SessionInfo info = (SessionInfo) sessionInfoBundleable.get();
+            this.mSessionId = info.mSessionId;
+            this.mDisplayType = info.mDisplayType;
+        } catch (BundlerException e) {
+            throw new IllegalArgumentException(
+                    "Expected the SessionInfo to be encoded in the bind intent extras, but they "
+                            + "were encoded improperly", e);
+        }
+    }
+
+    /**
+     * Adds the {@link SessionInfo} and associated identifier on the passed {@code intent} to
+     * pass to this service on bind.
+     */
+    public static void setBindData(@NonNull Intent intent, @NonNull SessionInfo sessionInfo) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            Api29.setIdentifier(intent, sessionInfo.toString());
+        } else {
+            intent.setData(new Uri.Builder().path(sessionInfo.toString()).build());
+        }
+        try {
+            intent.putExtra(EXTRA_SESSION_INFO, Bundleable.create(sessionInfo));
+        } catch (BundlerException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     // Required for Bundler
     private SessionInfo() {
         mSessionId = "main";
@@ -153,4 +212,25 @@
         return this.getSessionId().equals(object.getSessionId())
                 && this.getDisplayType() == object.getDisplayType();
     }
+
+    /** Android Q method calls wrapped in a {@link RequiresApi} class to appease the compiler. */
+    @RequiresApi(Build.VERSION_CODES.Q)
+    private static class Api29 {
+        // Not instantiable
+        private Api29() {
+        }
+
+        /** Wrapper for {@link Intent#getIdentifier()}. */
+        @DoNotInline
+        @Nullable
+        static String getIdentifier(@NonNull Intent intent) {
+            return intent.getIdentifier();
+        }
+
+        /** Wrapper for {@link Intent#setIdentifier(String)}. */
+        @DoNotInline
+        static void setIdentifier(@NonNull Intent intent, @NonNull String identifier) {
+            intent.setIdentifier(identifier);
+        }
+    }
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
index 63c5c9c..f21bd8d 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
@@ -51,6 +51,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -196,6 +197,7 @@
     }
 
     @SuppressLint("NewApi")
+    @Ignore // b/238635208
     @Test
     public void onAppCreate_updatesTheConfiguration() {
         Configuration configuration = new Configuration();
@@ -250,6 +252,7 @@
     }
 
     @SuppressLint("NewApi")
+    @Ignore // b/238635208
     @Test
     public void onConfigurationChanged_updatesTheConfiguration() throws RemoteException {
         mCarAppBinder.onAppCreate(mMockCarHost, null, new Configuration(),
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index 3ea8abb..bb49c17 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -55,6 +55,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -260,6 +261,7 @@
         assertThat(name).isNull();
     }
 
+    @Ignore // b/238635208
     @Test
     public void onConfigurationChanged_updatesTheConfiguration() {
         Configuration configuration = new Configuration();
diff --git a/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java b/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
index 3712ac8..1c3a502 100644
--- a/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
@@ -16,17 +16,24 @@
 
 package androidx.car.app;
 
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
 import static androidx.car.app.SessionInfo.DISPLAY_TYPE_CLUSTER;
 import static androidx.car.app.SessionInfo.DISPLAY_TYPE_MAIN;
+import static androidx.car.app.SessionInfo.EXTRA_SESSION_INFO;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Build;
+
 import androidx.car.app.model.Template;
 import androidx.car.app.versioning.CarAppApiLevels;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Set;
@@ -122,4 +129,50 @@
                 anotherDisplayTypeMainSessionId.toString());
     }
 
+    @Test
+    public void bind_insertsExtra() {
+        Intent intent = new Intent();
+        SessionInfo.setBindData(intent, DEFAULT_SESSION_INFO);
+
+        assertThat(intent.hasExtra(EXTRA_SESSION_INFO));
+    }
+
+    @Test
+    public void bind_from_returnsExpectedValue() {
+        Intent intent = new Intent();
+        SessionInfo.setBindData(intent, DEFAULT_SESSION_INFO);
+
+        SessionInfo info = new SessionInfo(intent);
+
+        assertThat(info).isEqualTo(DEFAULT_SESSION_INFO);
+
+    }
+
+    @SuppressLint("NewApi")
+    @Test
+    @Config(sdk = Build.VERSION_CODES.Q)
+    public void setBindData() {
+        SessionInfo testSessionInfo =
+                new SessionInfo(DISPLAY_TYPE_CLUSTER, "a-unique-session-id");
+        Intent intent = new Intent();
+        SessionInfo.setBindData(intent, testSessionInfo);
+
+        SessionInfo resultSessionInfo = new SessionInfo(intent);
+        assertThat(resultSessionInfo).isEqualTo(testSessionInfo);
+        assertThat(intent.getIdentifier()).isEqualTo(testSessionInfo.toString());
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.P)
+    public void setBindData_belowQ() {
+        SessionInfo testSessionInfo =
+                new SessionInfo(DISPLAY_TYPE_CLUSTER, "a-unique-session-id");
+        Intent intent = new Intent();
+        SessionInfo.setBindData(intent, testSessionInfo);
+
+        SessionInfo resultSessionInfo = new SessionInfo(intent);
+        assertThat(resultSessionInfo).isEqualTo(testSessionInfo);
+        assertThat(intent.getDataString()).isEqualTo(testSessionInfo.toString());
+    }
+
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java b/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
index 7e55652..46ae9a4 100644
--- a/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
@@ -39,6 +39,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -94,6 +95,7 @@
                 mDefaultPendingIntent);
     }
 
+    @Ignore // b/238635208
     @SuppressWarnings("deprecation")
     @Test
     public void updateForCar_embedded_overridesAsExpected() {
@@ -122,6 +124,7 @@
         assertThat(action.title).isEqualTo(EXTENDED_ACTION_TITLE);
     }
 
+    @Ignore // b/238635208
     @SuppressWarnings("deprecation")
     @Test
     public void updateForCar_notEmbedded_returnsTheSame() {
@@ -147,6 +150,7 @@
         assertThat(action.title).isEqualTo(DEFAULT_ACTION_TITLE);
     }
 
+    @Ignore // b/238635208
     @SuppressWarnings("deprecation")
     @Test
     public void updateForCar_notExtended_marksAsExtended()
diff --git a/collection/collection/build.gradle b/collection/collection/build.gradle
index af379cf..784367d 100644
--- a/collection/collection/build.gradle
+++ b/collection/collection/build.gradle
@@ -36,6 +36,7 @@
     macosX64()
     macosArm64()
     linuxX64()
+    ios()
 
     sourceSets {
         commonMain {
@@ -71,7 +72,8 @@
         targets.all { target ->
             if (target.platformType == KotlinPlatformType.native) {
                 target.compilations["main"].defaultSourceSet {
-                    if (target.konanTarget.family == Family.OSX) {
+                    if (target.konanTarget.family == Family.OSX ||
+                        target.konanTarget.family == Family.IOS) {
                         dependsOn(darwinMain)
                     } else if (target.konanTarget.family == Family.LINUX) {
                         dependsOn(linuxMain)
@@ -127,6 +129,7 @@
         jvm = Publish.SNAPSHOT_AND_RELEASE
         linux = Publish.SNAPSHOT_AND_RELEASE
         mac = Publish.SNAPSHOT_AND_RELEASE
+        ios = Publish.SNAPSHOT_AND_RELEASE
     }
     mavenGroup = LibraryGroups.COLLECTION
     inceptionYear = "2018"
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 44c0a94..ed94995 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -204,7 +204,7 @@
             project: Project,
             configuration: CompilerConfiguration
         ) {
-            val KOTLIN_VERSION_EXPECTATION = "1.7.0"
+            val KOTLIN_VERSION_EXPECTATION = "1.7.10"
             KotlinCompilerVersion.getVersion()?.let { version ->
                 val suppressKotlinVersionCheck = configuration.get(
                     ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index b66d0de..7b54efd 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -631,7 +631,7 @@
         // soon be _more_ visible, so don't scroll.
         if (!containerShrunk) return
 
-        val focusedChild = focusedChild ?: return
+        val focusedChild = focusedChild?.takeIf { it.isAttached } ?: return
         val focusedBounds = coordinates.localBoundingBoxOf(focusedChild, clipBounds = false)
 
         // In order to check if we need to scroll to bring the focused child into view, it's not
diff --git a/compose/ui/ui-geometry/api/current.txt b/compose/ui/ui-geometry/api/current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/current.txt
+++ b/compose/ui/ui-geometry/api/current.txt
@@ -32,7 +32,7 @@
 
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public float getBottom();
     method public inline float getHeight();
     method public float getLeft();
@@ -105,7 +105,7 @@
     method public float component2();
     method public float component3();
     method public float component4();
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
     method public float getBottom();
@@ -180,7 +180,7 @@
     method public long component6-kKHJgLs();
     method public long component7-kKHJgLs();
     method public long component8-kKHJgLs();
-    method public boolean contains(long point);
+    method public operator boolean contains(long point);
     method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
     method public float getBottom();
     method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
@@ -32,7 +32,7 @@
 
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public float getBottom();
     method public inline float getHeight();
     method public float getLeft();
@@ -105,7 +105,7 @@
     method public float component2();
     method public float component3();
     method public float component4();
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
     method public float getBottom();
@@ -180,7 +180,7 @@
     method public long component6-kKHJgLs();
     method public long component7-kKHJgLs();
     method public long component8-kKHJgLs();
-    method public boolean contains(long point);
+    method public operator boolean contains(long point);
     method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
     method public float getBottom();
     method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/api/restricted_current.txt b/compose/ui/ui-geometry/api/restricted_current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -32,7 +32,7 @@
 
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public float getBottom();
     method public inline float getHeight();
     method public float getLeft();
@@ -105,7 +105,7 @@
     method public float component2();
     method public float component3();
     method public float component4();
-    method public boolean contains(long offset);
+    method public operator boolean contains(long offset);
     method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
     method public float getBottom();
@@ -180,7 +180,7 @@
     method public long component6-kKHJgLs();
     method public long component7-kKHJgLs();
     method public long component8-kKHJgLs();
-    method public boolean contains(long point);
+    method public operator boolean contains(long point);
     method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
     method public float getBottom();
     method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
index b00c587..1e2d5b1 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
@@ -77,7 +77,7 @@
      * Rectangles include their top and left edges but exclude their bottom and
      * right edges.
      */
-    fun contains(offset: Offset): Boolean {
+    operator fun contains(offset: Offset): Boolean {
         return offset.x >= left && offset.x < right && offset.y >= top && offset.y < bottom
     }
 
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
index 6c976ed..5230b42 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
@@ -244,7 +244,7 @@
      * Rectangles include their top and left edges but exclude their bottom and
      * right edges.
      */
-    fun contains(offset: Offset): Boolean {
+    operator fun contains(offset: Offset): Boolean {
         return offset.x >= left && offset.x < right && offset.y >= top && offset.y < bottom
     }
 
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
index 9e1484c..2ce06a3 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
@@ -122,7 +122,7 @@
      * using this method, prefer to reuse existing [RoundRect]s rather than
      * recreating the object each time.
      */
-    fun contains(point: Offset): Boolean {
+    operator fun contains(point: Offset): Boolean {
         if (point.x < left || point.x >= right || point.y < top || point.y >= bottom) {
             return false; // outside bounding box
         }
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
index 1750492..ce9a9c9 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
@@ -51,18 +51,18 @@
     @Test
     fun contains() {
         val r = MutableRect(1f, 3f, 5f, 9f)
-        assertTrue(r.contains(Offset(1f, 3f)))
-        assertTrue(r.contains(Offset(3f, 3f)))
-        assertFalse(r.contains(Offset(5f, 3f)))
-        assertTrue(r.contains(Offset(1f, 6f)))
-        assertTrue(r.contains(Offset(3f, 6f)))
-        assertFalse(r.contains(Offset(5f, 6f)))
-        assertFalse(r.contains(Offset(1f, 9f)))
-        assertFalse(r.contains(Offset(3f, 9f)))
-        assertFalse(r.contains(Offset(5f, 9f)))
-        assertFalse(r.contains(Offset(0f, 0f)))
-        assertFalse(r.contains(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)))
-        assertFalse(r.contains(Offset(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)))
+        assertTrue(Offset(1f, 3f) in r)
+        assertTrue(Offset(3f, 3f) in r)
+        assertFalse(Offset(5f, 3f) in r)
+        assertTrue(Offset(1f, 6f) in r)
+        assertTrue(Offset(3f, 6f) in r)
+        assertFalse(Offset(5f, 6f) in r)
+        assertFalse(Offset(1f, 9f) in r)
+        assertFalse(Offset(3f, 9f) in r)
+        assertFalse(Offset(5f, 9f) in r)
+        assertFalse(Offset(0f, 0f) in r)
+        assertFalse(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) in r)
+        assertFalse(Offset(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY) in r)
     }
 
     @Test
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
index b2e5432..927b408 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
@@ -223,17 +223,17 @@
     fun `rect contains`() {
         val rect = Rect(100f, 10f, 200f, 300f)
         val offset = Offset(177f, 288f)
-        assertTrue(rect.contains(offset))
+        assertTrue(offset in rect)
     }
 
     @Test
     fun `rect does not contain`() {
         val rect = Rect(100f, 10f, 200f, 300f)
         val offset1 = Offset(201f, 150f)
-        assertFalse(rect.contains(offset1))
+        assertFalse(offset1 in rect)
 
         val offset2 = Offset(200f, 301f)
-        assertFalse(rect.contains(offset2))
+        assertFalse(offset2 in rect)
     }
 
     @Test
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
index 0880e8e..1f6aeac 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
@@ -36,14 +36,14 @@
             bottomLeft = CornerRadius.Zero
         )
 
-        assertFalse(roundRect.contains(Offset(1.0f, 1.0f)))
-        assertFalse(roundRect.contains(Offset(1.1f, 1.1f)))
-        assertTrue(roundRect.contains(Offset(1.15f, 1.15f)))
-        assertFalse(roundRect.contains(Offset(2.0f, 1.0f)))
-        assertFalse(roundRect.contains(Offset(1.93f, 1.07f)))
-        assertFalse(roundRect.contains(Offset(1.97f, 1.7f)))
-        assertTrue(roundRect.contains(Offset(1.7f, 1.97f)))
-        assertTrue(roundRect.contains(Offset(1.0f, 1.99f)))
+        assertFalse(Offset(1.0f, 1.0f) in roundRect)
+        assertFalse(Offset(1.1f, 1.1f) in roundRect)
+        assertTrue(Offset(1.15f, 1.15f) in roundRect)
+        assertFalse(Offset(2.0f, 1.0f) in roundRect)
+        assertFalse(Offset(1.93f, 1.07f) in roundRect)
+        assertFalse(Offset(1.97f, 1.7f) in roundRect)
+        assertTrue(Offset(1.7f, 1.97f) in roundRect)
+        assertTrue(Offset(1.0f, 1.99f) in roundRect)
     }
 
     @Test
@@ -56,14 +56,14 @@
             bottomLeft = CornerRadius.Zero
         )
 
-        assertFalse(roundRect.contains(Offset(1.0f, 1.0f)))
-        assertFalse(roundRect.contains(Offset(1.1f, 1.1f)))
-        assertTrue(roundRect.contains(Offset(1.15f, 1.15f)))
-        assertFalse(roundRect.contains(Offset(2.0f, 1.0f)))
-        assertFalse(roundRect.contains(Offset(1.93f, 1.07f)))
-        assertFalse(roundRect.contains(Offset(1.97f, 1.7f)))
-        assertTrue(roundRect.contains(Offset(1.7f, 1.97f)))
-        assertTrue(roundRect.contains(Offset(1.0f, 1.99f)))
+        assertFalse(Offset(1.0f, 1.0f) in roundRect)
+        assertFalse(Offset(1.1f, 1.1f) in roundRect)
+        assertTrue(Offset(1.15f, 1.15f) in roundRect)
+        assertFalse(Offset(2.0f, 1.0f) in roundRect)
+        assertFalse(Offset(1.93f, 1.07f) in roundRect)
+        assertFalse(Offset(1.97f, 1.7f) in roundRect)
+        assertTrue(Offset(1.7f, 1.97f) in roundRect)
+        assertTrue(Offset(1.0f, 1.99f) in roundRect)
     }
 
     @Test
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 8ea7c85..5adc1c1 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -220,6 +220,7 @@
         assertTrue(info.isHeading)
         assertTrue(info.isClickable)
         assertTrue(info.isVisibleToUser)
+        assertTrue(info.isImportantForAccessibility)
     }
 
     @Test
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 4775e3a..ad153c9 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
@@ -431,6 +431,12 @@
 
         info.packageName = view.context.packageName
 
+        // This property exists to distinguish semantically meaningful nodes from purely structural
+        // or decorative UI elements. In Compose, LayoutNodes without semantics are simply omitted
+        // from the AccessibilityNodeInfo tree. Therefore, every AccessibilityNodeInfo qualifies as
+        // "important".
+        info.isImportantForAccessibility = true
+
         semanticsNode.replacedChildrenSortedByBounds.fastForEach { child ->
             if (currentSemanticsNodes.contains(child.id)) {
                 val holder = view.androidViewsHandler.layoutNodeToHolder[child.layoutNode]
diff --git a/core/core-ktx/api/current.txt b/core/core-ktx/api/current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/current.txt
+++ b/core/core-ktx/api/current.txt
@@ -563,6 +563,7 @@
     method public static inline boolean isNotEmpty(android.view.Menu);
     method public static operator java.util.Iterator iterator(android.view.Menu);
     method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+    method public static inline void removeItemAt(android.view.Menu, int index);
   }
 
   public final class ViewGroupKt {
diff --git a/core/core-ktx/api/public_plus_experimental_current.txt b/core/core-ktx/api/public_plus_experimental_current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/public_plus_experimental_current.txt
+++ b/core/core-ktx/api/public_plus_experimental_current.txt
@@ -563,6 +563,7 @@
     method public static inline boolean isNotEmpty(android.view.Menu);
     method public static operator java.util.Iterator iterator(android.view.Menu);
     method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+    method public static inline void removeItemAt(android.view.Menu, int index);
   }
 
   public final class ViewGroupKt {
diff --git a/core/core-ktx/api/restricted_current.txt b/core/core-ktx/api/restricted_current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/restricted_current.txt
+++ b/core/core-ktx/api/restricted_current.txt
@@ -563,6 +563,7 @@
     method public static inline boolean isNotEmpty(android.view.Menu);
     method public static operator java.util.Iterator iterator(android.view.Menu);
     method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+    method public static inline void removeItemAt(android.view.Menu, int index);
   }
 
   public final class ViewGroupKt {
diff --git a/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt b/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
index 6e195ce..9d5482a 100644
--- a/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
+++ b/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
@@ -136,8 +136,8 @@
     }
 
     @Test fun iteratorRemoving() {
-        val item1 = menu.add("")
-        val item2 = menu.add("")
+        val item1 = menu.add(NONE, 9, NONE, "")
+        val item2 = menu.add(NONE, 13, NONE, "")
 
         val iterator = menu.iterator()
 
@@ -163,4 +163,17 @@
             assertSame(items[index], child)
         }
     }
+
+    @Test fun removeItemAt() {
+        val item1 = menu.add(NONE, 9, NONE, "")
+        val item2 = menu.add(NONE, 13, NONE, "")
+
+        menu.removeItemAt(0)
+        assertFalse(item1 in menu)
+        assertEquals(1, menu.size())
+
+        menu.removeItemAt(0)
+        assertFalse(item2 in menu)
+        assertEquals(0, menu.size())
+    }
 }
diff --git a/core/core-ktx/src/main/java/androidx/core/view/Menu.kt b/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
index 804c64b..91095ce 100644
--- a/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
+++ b/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
@@ -71,9 +71,17 @@
         private var index = 0
         override fun hasNext() = index < size()
         override fun next() = getItem(index++) ?: throw IndexOutOfBoundsException()
-        override fun remove() = removeItem(--index)
+        override fun remove() = removeItemAt(--index)
     }
 
+/**
+ * Removes the menu item at the specified index.
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+public inline fun Menu.removeItemAt(index: Int) =
+    getItem(index)?.let { removeItem(it.itemId) } ?: throw IndexOutOfBoundsException()
+
 /** Returns a [Sequence] over the items in this menu. */
 public val Menu.children: Sequence
     get() = object : Sequence {
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 45e2858..f3eac5e 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -238,6 +238,7 @@
 Found an unresolved type in androidx\.slice\.builders\$list\(android\.content\.Context, android\.net\.Uri, kotlin\.Long, kotlin\.Function[0-9]+\(\(androidx\.slice\.builders\.ListBuilderDsl, kotlin\.Unit\)\)\) \(ListBuilder\.kt:[0-9]+\)
 # See b/180023439 for hiltNavGraphViewModel warning.
 Unresolved link to .*
+Found an unresolved type in androidx\.core\.view\$removeItemAt\(android\.view\.Menu, kotlin\.Int\) \(Menu\.kt:[0-9]+\)
 Found an unresolved type in androidx\.compose\.foundation\.MutatorMutex\$mutate\(androidx\.compose\.foundation\.MutatePriority, kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.MutatorMutex\.mutate\.R\)\)\) \(MutatorMutex\.kt:[0-9]+\)
 Found an unresolved type in androidx\.compose\.foundation\.MutatorMutex\$mutateWith\(androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.T, androidx\.compose\.foundation\.MutatePriority, kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.T, androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.R\)\)\) \(MutatorMutex\.kt:[0-9]+\)
 Found an unresolved type in androidx\.compose\.foundation\.gestures\$detectTapGestures\(androidx\.compose\.ui\.input\.pointer\.PointerInputScope, kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.gestures\.PressGestureScope, androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\)\) \(TapGestureDetector\.kt:[0-9]+\)
@@ -432,8 +433,7 @@
 # Linux builds cannot build mac, hence we get this warning.
 # see: https://github.com/JetBrains/kotlin/blob/master/native/commonizer/README.md
 # This warning is printed from: https://github.com/JetBrains/kotlin/blob/bc853e45e8982eff74e3263b0197c1af6086615d/native/commonizer/src/org/jetbrains/kotlin/commonizer/konan/LibraryCommonizer.kt#L41
-Warning\: No libraries found for target macos_arm[0-9]+\. This target will be excluded from commonization\.
-Warning\: No libraries found for target macos_x[0-9]+\. This target will be excluded from commonization\.
+Warning\: No libraries found for target (macos|ios|ios_simulator)_(arm|x)[0-9]+\. This target will be excluded from commonization\.
 # > Task :tv:tv-foundation:processDebugManifest
 # > Task :tv:tv-material:processDebugManifest
 # > Task :tv:tv-material:processReleaseManifest
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 630cc0f..a707641 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -30,11 +30,11 @@
 guavaJre = "31.1-jre"
 hilt = "2.42"
 incap = "0.2"
-kotlin = "1.7.0"
-kotlinNative = "1.7.0"
+kotlin = "1.7.10"
+kotlinNative = "1.7.10"
 kotlinCompileTesting = "1.4.1"
 kotlinCoroutines = "1.6.1"
-ksp = "1.7.0-1.0.6"
+ksp = "1.7.10-1.0.6"
 ktlint = "0.46.0-20220520.192227-74"
 leakcanary = "2.8.1"
 metalava = "1.0.0-alpha06"
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 0cbbb70..ea6f9642 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1238,21 +1238,21 @@
          
       
       
-      
-         
-            
+      
+         
+            
          
       
 
-      
-         
-            
+      
+         
+            
          
       
 
-      
-         
-            
+      
+         
+            
          
       
    
diff --git a/health/health-connect-client/api/current.txt b/health/health-connect-client/api/current.txt
index 57d6ee29..a6a0214 100644
--- a/health/health-connect-client/api/current.txt
+++ b/health/health-connect-client/api/current.txt
@@ -162,7 +162,7 @@
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
-    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
     field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
   }
 
@@ -323,26 +323,18 @@
     field public static final String MEDIUM = "medium";
   }
 
-  public final class CyclingPedalingCadence {
-    ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
-    method public double getRevolutionsPerMinute();
-    method public java.time.Instant getTime();
-    property public final double revolutionsPerMinute;
-    property public final java.time.Instant time;
-  }
-
   public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.Record {
-    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
   public static final class CyclingPedalingCadenceRecord.Companion {
   }
 
+  public static final class CyclingPedalingCadenceRecord.Sample {
+    ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+    method public double getRevolutionsPerMinute();
+    method public java.time.Instant getTime();
+    property public final double revolutionsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class DistanceRecord implements androidx.health.connect.client.records.Record {
     ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
   public static final class FloorsClimbedRecord.Companion {
   }
 
-  public final class HeartRate {
-    ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
-    method public long getBeatsPerMinute();
-    method public java.time.Instant getTime();
-    property public final long beatsPerMinute;
-    property public final java.time.Instant time;
-  }
-
   public final class HeartRateRecord implements androidx.health.connect.client.records.Record {
-    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
   public static final class HeartRateRecord.Companion {
   }
 
+  public static final class HeartRateRecord.Sample {
+    ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+    method public long getBeatsPerMinute();
+    method public java.time.Instant getTime();
+    property public final long beatsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class HeightRecord implements androidx.health.connect.client.records.Record {
     ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getHeight();
@@ -1099,26 +1099,18 @@
     property public final java.time.Instant time;
   }
 
-  public final class StepsCadence {
-    ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
-    method public double getRate();
-    method public java.time.Instant getTime();
-    property public final double rate;
-    property public final java.time.Instant time;
-  }
-
   public final class StepsCadenceRecord implements androidx.health.connect.client.records.Record {
-    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1130,6 +1122,14 @@
   public static final class StepsCadenceRecord.Companion {
   }
 
+  public static final class StepsCadenceRecord.Sample {
+    ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+    method public double getRate();
+    method public java.time.Instant getTime();
+    property public final double rate;
+    property public final java.time.Instant time;
+  }
+
   public final class StepsRecord implements androidx.health.connect.client.records.Record {
     ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
diff --git a/health/health-connect-client/api/public_plus_experimental_current.txt b/health/health-connect-client/api/public_plus_experimental_current.txt
index 57d6ee29..a6a0214 100644
--- a/health/health-connect-client/api/public_plus_experimental_current.txt
+++ b/health/health-connect-client/api/public_plus_experimental_current.txt
@@ -162,7 +162,7 @@
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
-    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
     field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
   }
 
@@ -323,26 +323,18 @@
     field public static final String MEDIUM = "medium";
   }
 
-  public final class CyclingPedalingCadence {
-    ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
-    method public double getRevolutionsPerMinute();
-    method public java.time.Instant getTime();
-    property public final double revolutionsPerMinute;
-    property public final java.time.Instant time;
-  }
-
   public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.Record {
-    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
   public static final class CyclingPedalingCadenceRecord.Companion {
   }
 
+  public static final class CyclingPedalingCadenceRecord.Sample {
+    ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+    method public double getRevolutionsPerMinute();
+    method public java.time.Instant getTime();
+    property public final double revolutionsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class DistanceRecord implements androidx.health.connect.client.records.Record {
     ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
   public static final class FloorsClimbedRecord.Companion {
   }
 
-  public final class HeartRate {
-    ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
-    method public long getBeatsPerMinute();
-    method public java.time.Instant getTime();
-    property public final long beatsPerMinute;
-    property public final java.time.Instant time;
-  }
-
   public final class HeartRateRecord implements androidx.health.connect.client.records.Record {
-    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
   public static final class HeartRateRecord.Companion {
   }
 
+  public static final class HeartRateRecord.Sample {
+    ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+    method public long getBeatsPerMinute();
+    method public java.time.Instant getTime();
+    property public final long beatsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class HeightRecord implements androidx.health.connect.client.records.Record {
     ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getHeight();
@@ -1099,26 +1099,18 @@
     property public final java.time.Instant time;
   }
 
-  public final class StepsCadence {
-    ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
-    method public double getRate();
-    method public java.time.Instant getTime();
-    property public final double rate;
-    property public final java.time.Instant time;
-  }
-
   public final class StepsCadenceRecord implements androidx.health.connect.client.records.Record {
-    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1130,6 +1122,14 @@
   public static final class StepsCadenceRecord.Companion {
   }
 
+  public static final class StepsCadenceRecord.Sample {
+    ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+    method public double getRate();
+    method public java.time.Instant getTime();
+    property public final double rate;
+    property public final java.time.Instant time;
+  }
+
   public final class StepsRecord implements androidx.health.connect.client.records.Record {
     ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
diff --git a/health/health-connect-client/api/restricted_current.txt b/health/health-connect-client/api/restricted_current.txt
index 27ca15d..9dd1f89 100644
--- a/health/health-connect-client/api/restricted_current.txt
+++ b/health/health-connect-client/api/restricted_current.txt
@@ -162,7 +162,7 @@
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
-    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+    field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
     field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
   }
 
@@ -323,26 +323,18 @@
     field public static final String MEDIUM = "medium";
   }
 
-  public final class CyclingPedalingCadence {
-    ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
-    method public double getRevolutionsPerMinute();
-    method public java.time.Instant getTime();
-    property public final double revolutionsPerMinute;
-    property public final java.time.Instant time;
-  }
-
-  public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
-    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+  public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
+    ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
   public static final class CyclingPedalingCadenceRecord.Companion {
   }
 
+  public static final class CyclingPedalingCadenceRecord.Sample {
+    ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+    method public double getRevolutionsPerMinute();
+    method public java.time.Instant getTime();
+    property public final double revolutionsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class DistanceRecord implements androidx.health.connect.client.records.IntervalRecord {
     ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
   public static final class FloorsClimbedRecord.Companion {
   }
 
-  public final class HeartRate {
-    ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
-    method public long getBeatsPerMinute();
-    method public java.time.Instant getTime();
-    property public final long beatsPerMinute;
-    property public final java.time.Instant time;
-  }
-
-  public final class HeartRateRecord implements androidx.health.connect.client.records.SeriesRecord {
-    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+  public final class HeartRateRecord implements androidx.health.connect.client.records.SeriesRecord {
+    ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
   public static final class HeartRateRecord.Companion {
   }
 
+  public static final class HeartRateRecord.Sample {
+    ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+    method public long getBeatsPerMinute();
+    method public java.time.Instant getTime();
+    property public final long beatsPerMinute;
+    property public final java.time.Instant time;
+  }
+
   public final class HeightRecord implements androidx.health.connect.client.records.InstantaneousRecord {
     ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.units.Length getHeight();
@@ -1122,26 +1122,18 @@
     property public final java.time.Instant time;
   }
 
-  public final class StepsCadence {
-    ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
-    method public double getRate();
-    method public java.time.Instant getTime();
-    property public final double rate;
-    property public final java.time.Instant time;
-  }
-
-  public final class StepsCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
-    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+  public final class StepsCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
+    ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public java.util.List getSamples();
+    method public java.util.List getSamples();
     method public java.time.Instant getStartTime();
     method public java.time.ZoneOffset? getStartZoneOffset();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public java.util.List samples;
+    property public java.util.List samples;
     property public java.time.Instant startTime;
     property public java.time.ZoneOffset? startZoneOffset;
     field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1153,6 +1145,14 @@
   public static final class StepsCadenceRecord.Companion {
   }
 
+  public static final class StepsCadenceRecord.Sample {
+    ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+    method public double getRate();
+    method public java.time.Instant getTime();
+    property public final double rate;
+    property public final java.time.Instant time;
+  }
+
   public final class StepsRecord implements androidx.health.connect.client.records.IntervalRecord {
     ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index f767c8b..41d9897 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -28,7 +28,6 @@
 import androidx.health.connect.client.records.BodyWaterMassRecord
 import androidx.health.connect.client.records.BoneMassRecord
 import androidx.health.connect.client.records.CervicalMucusRecord
-import androidx.health.connect.client.records.CyclingPedalingCadence
 import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
@@ -37,7 +36,6 @@
 import androidx.health.connect.client.records.ExerciseRepetitionsRecord
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
-import androidx.health.connect.client.records.HeartRate
 import androidx.health.connect.client.records.HeartRateRecord
 import androidx.health.connect.client.records.HeartRateVariabilityDifferentialIndexRecord
 import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
@@ -64,7 +62,6 @@
 import androidx.health.connect.client.records.SleepSessionRecord
 import androidx.health.connect.client.records.SleepStageRecord
 import androidx.health.connect.client.records.SpeedRecord
-import androidx.health.connect.client.records.StepsCadence
 import androidx.health.connect.client.records.StepsCadenceRecord
 import androidx.health.connect.client.records.StepsRecord
 import androidx.health.connect.client.records.SwimmingStrokesRecord
@@ -171,7 +168,7 @@
                     endZoneOffset = endZoneOffset,
                     samples =
                         seriesValuesList.map { value ->
-                            CyclingPedalingCadence(
+                            CyclingPedalingCadenceRecord.Sample(
                                 time = Instant.ofEpochMilli(value.instantTimeMillis),
                                 revolutionsPerMinute = value.getDouble("rpm"),
                             )
@@ -186,7 +183,7 @@
                     endZoneOffset = endZoneOffset,
                     samples =
                         seriesValuesList.map { value ->
-                            HeartRate(
+                            HeartRateRecord.Sample(
                                 time = Instant.ofEpochMilli(value.instantTimeMillis),
                                 beatsPerMinute = value.getLong("bpm"),
                             )
@@ -357,7 +354,7 @@
                     endZoneOffset = endZoneOffset,
                     samples =
                         seriesValuesList.map { value ->
-                            StepsCadence(
+                            StepsCadenceRecord.Sample(
                                 time = Instant.ofEpochMilli(value.instantTimeMillis),
                                 rate = value.getDouble("rate"),
                             )
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
index 156351e..4550a2b 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
@@ -17,6 +17,7 @@
 
 import androidx.health.connect.client.aggregate.AggregateMetric
 import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Energy
 import androidx.health.connect.client.units.Power
 import java.time.Instant
 import java.time.ZoneOffset
@@ -66,12 +67,12 @@
          * [androidx.health.connect.client.aggregate.AggregationResult].
          */
         @JvmField
-        val BASAL_CALORIES_TOTAL: AggregateMetric =
+        val BASAL_CALORIES_TOTAL: AggregateMetric =
             AggregateMetric.doubleMetric(
                 dataTypeName = BASAL_CALORIES_TYPE_NAME,
                 aggregationType = AggregateMetric.AggregationType.TOTAL,
                 fieldName = ENERGY_FIELD_NAME,
-                mapper = Power::kilocaloriesPerDay,
+                mapper = Energy::calories,
             )
     }
 }
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
index c8ba7be..0c87da2 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
@@ -33,9 +33,9 @@
     override val startZoneOffset: ZoneOffset?,
     override val endTime: Instant,
     override val endZoneOffset: ZoneOffset?,
-    override val samples: List,
+    override val samples: List,
     override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
 
     /*
      * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -89,44 +89,44 @@
          */
         @JvmField val RPM_MAX: AggregateMetric = doubleMetric(TYPE, MAXIMUM, RPM_FIELD)
     }
-}
 
-/**
- * Represents a single measurement of the cycling pedaling cadence.
- *
- * @param time The point in time when the measurement was taken.
- * @param revolutionsPerMinute Cycling revolutions per minute. Valid range: 0-10000.
- *
- * @see CyclingPedalingCadenceRecord
- */
-public class CyclingPedalingCadence(
-    val time: Instant,
-    @FloatRange(from = 0.0, to = 10_000.0) val revolutionsPerMinute: Double,
-) {
-
-    init {
-        requireNonNegative(value = revolutionsPerMinute, name = "revolutionsPerMinute")
-    }
-
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+    /**
+     * Represents a single measurement of the cycling pedaling cadence.
+     *
+     * @param time The point in time when the measurement was taken.
+     * @param revolutionsPerMinute Cycling revolutions per minute. Valid range: 0-10000.
+     *
+     * @see CyclingPedalingCadenceRecord
      */
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is CyclingPedalingCadence) return false
+    public class Sample(
+        val time: Instant,
+        @FloatRange(from = 0.0, to = 10_000.0) val revolutionsPerMinute: Double,
+    ) {
 
-        if (time != other.time) return false
-        if (revolutionsPerMinute != other.revolutionsPerMinute) return false
+        init {
+            requireNonNegative(value = revolutionsPerMinute, name = "revolutionsPerMinute")
+        }
 
-        return true
-    }
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Sample) return false
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = time.hashCode()
-        result = 31 * result + revolutionsPerMinute.hashCode()
-        return result
+            if (time != other.time) return false
+            if (revolutionsPerMinute != other.revolutionsPerMinute) return false
+
+            return true
+        }
+
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun hashCode(): Int {
+            var result = time.hashCode()
+            result = 31 * result + revolutionsPerMinute.hashCode()
+            return result
+        }
     }
 }
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
index b16f820..bd488fe 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
@@ -27,9 +27,9 @@
     override val startZoneOffset: ZoneOffset?,
     override val endTime: Instant,
     override val endZoneOffset: ZoneOffset?,
-    override val samples: List,
+    override val samples: List,
     override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
 
     /*
      * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -100,44 +100,44 @@
         val MEASUREMENTS_COUNT: AggregateMetric =
             AggregateMetric.countMetric(HEART_RATE_TYPE_NAME)
     }
-}
 
-/**
- * Represents a single measurement of the heart rate.
- *
- * @param time The point in time when the measurement was taken.
- * @param beatsPerMinute Heart beats per minute. Validation range: 1-300.
- *
- * @see HeartRateRecord
- */
-public class HeartRate(
-    val time: Instant,
-    @androidx.annotation.IntRange(from = 1, to = 300) val beatsPerMinute: Long,
-) {
-
-    init {
-        requireNonNegative(value = beatsPerMinute, name = "beatsPerMinute")
-    }
-
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+    /**
+     * Represents a single measurement of the heart rate.
+     *
+     * @param time The point in time when the measurement was taken.
+     * @param beatsPerMinute Heart beats per minute. Validation range: 1-300.
+     *
+     * @see HeartRateRecord
      */
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is HeartRate) return false
+    public class Sample(
+        val time: Instant,
+        @androidx.annotation.IntRange(from = 1, to = 300) val beatsPerMinute: Long,
+    ) {
 
-        if (time != other.time) return false
-        if (beatsPerMinute != other.beatsPerMinute) return false
+        init {
+            requireNonNegative(value = beatsPerMinute, name = "beatsPerMinute")
+        }
 
-        return true
-    }
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Sample) return false
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = time.hashCode()
-        result = 31 * result + beatsPerMinute.hashCode()
-        return result
+            if (time != other.time) return false
+            if (beatsPerMinute != other.beatsPerMinute) return false
+
+            return true
+        }
+
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun hashCode(): Int {
+            var result = time.hashCode()
+            result = 31 * result + beatsPerMinute.hashCode()
+            return result
+        }
     }
 }
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
index c4a2852..40299d1 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
@@ -20,9 +20,13 @@
 /** Type of meal. */
 public object MealType {
     const val UNKNOWN = "unknown"
+    /** Use this for the first meal of the day, usually the morning meal. */
     const val BREAKFAST = "breakfast"
+    /** Use this for the noon meal. */
     const val LUNCH = "lunch"
+    /** Use this for last meal of the day, usually the evening meal. */
     const val DINNER = "dinner"
+    /** Any meal outside of the usual three meals per day. */
     const val SNACK = "snack"
 }
 
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
index b10ccfe..d7488b6 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
@@ -31,9 +31,9 @@
     override val startZoneOffset: ZoneOffset?,
     override val endTime: Instant,
     override val endZoneOffset: ZoneOffset?,
-    override val samples: List,
+    override val samples: List,
     override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
 
     /*
      * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -87,42 +87,42 @@
          */
         @JvmField val RATE_MAX: AggregateMetric = doubleMetric(TYPE, MAXIMUM, RATE_FIELD)
     }
-}
 
-/**
- * Represents a single measurement of the steps cadence.
- *
- * @param time The point in time when the measurement was taken.
- * @param rate Rate in steps per minute. Valid range: 0-10000.
- */
-class StepsCadence(
-    val time: Instant,
-    @FloatRange(from = 0.0, to = 10_000.0) val rate: Double,
-) {
-
-    init {
-        requireNonNegative(value = rate, name = "rate")
-    }
-
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+    /**
+     * Represents a single measurement of the steps cadence.
+     *
+     * @param time The point in time when the measurement was taken.
+     * @param rate Rate in steps per minute. Valid range: 0-10000.
      */
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is StepsCadence) return false
+    class Sample(
+        val time: Instant,
+        @FloatRange(from = 0.0, to = 10_000.0) val rate: Double,
+    ) {
 
-        if (time != other.time) return false
-        if (rate != other.rate) return false
+        init {
+            requireNonNegative(value = rate, name = "rate")
+        }
 
-        return true
-    }
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Sample) return false
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = time.hashCode()
-        result = 31 * result + rate.hashCode()
-        return result
+            if (time != other.time) return false
+            if (rate != other.rate) return false
+
+            return true
+        }
+
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun hashCode(): Int {
+            var result = time.hashCode()
+            result = 31 * result + rate.hashCode()
+            return result
+        }
     }
 }
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
index 8c09819..cb606a9 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
@@ -24,6 +24,10 @@
  * Request object to read time bucketed aggregations for given [AggregateMetric]s in Android Health
  * Platform.
  *
+ * [timeRangeSlicer] contains a [Duration] of fixed physical time intervals, such as per hour, per
+ * ten minutes or so. Prefer [AggregateGroupByPeriodRequest], if you would like variable length time
+ * intervals, such as per day, which may or may not include DST (23 or 25 hour).
+ *
  * @param metrics Set of [AggregateMetric]s to aggregate, such as `Steps::STEPS_COUNT_TOTAL`.
  * @param timeRangeFilter The [TimeRangeFilter] to read from.
  * @param timeRangeSlicer The bucket size of each returned aggregate row. [timeRangeFilter] will be
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
index bae6286..00ca4b0 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
@@ -24,6 +24,10 @@
  * Request object to read time bucketed aggregations for given [AggregateMetric]s in Android Health
  * Platform.
  *
+ * [timeRangeSlicer] contains a [Period] of variable length time intervals, such as per day, which
+ * may be anywhere between 23, 24, or 25 hour. Use [AggregateGroupByDurationRequest] if time slice
+ * is of fixed intervals, such as an hour, every ten minutes.
+ *
  * @param metrics Set of [AggregateMetric]s to aggregate, such as `Steps::STEPS_COUNT_TOTAL`.
  * @param timeRangeFilter The [TimeRangeFilter] to read from.
  * @param timeRangeSlicer The bucket size of each returned aggregate row. [timeRangeFilter] will be
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
index ec02294..9df3d3c 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
@@ -22,10 +22,9 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
-import java.lang.IllegalStateException
-import java.lang.UnsupportedOperationException
 import org.junit.Assert.assertThrows
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.Shadows
@@ -43,6 +42,7 @@
         context = ApplicationProvider.getApplicationContext()
     }
 
+    @Ignore // b/238635208
     @Test
     fun noBackingImplementation_unavailable() {
         val packageManager = context.packageManager
@@ -54,6 +54,7 @@
         }
     }
 
+    @Ignore // b/238635208
     @Test
     fun backingImplementation_notEnabled_unavailable() {
         installPackage(context, PROVIDER_PACKAGE_NAME, enabled = false)
@@ -64,6 +65,7 @@
         }
     }
 
+    @Ignore // b/238635208
     @Test
     fun backingImplementation_enabled_isAvailable() {
         installPackage(context, PROVIDER_PACKAGE_NAME, enabled = true)
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index 2679086d..1510f7b 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -29,7 +29,6 @@
 import androidx.health.connect.client.records.CervicalMucusRecord
 import androidx.health.connect.client.records.CervicalMucusRecord.Appearance
 import androidx.health.connect.client.records.CervicalMucusRecord.Sensation
-import androidx.health.connect.client.records.CyclingPedalingCadence
 import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
@@ -40,7 +39,6 @@
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.ExerciseSessionRecord.ExerciseType
 import androidx.health.connect.client.records.FloorsClimbedRecord
-import androidx.health.connect.client.records.HeartRate
 import androidx.health.connect.client.records.HeartRateRecord
 import androidx.health.connect.client.records.HeartRateVariabilityDifferentialIndexRecord
 import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
@@ -70,7 +68,6 @@
 import androidx.health.connect.client.records.SleepStageRecord
 import androidx.health.connect.client.records.SleepStageRecord.StageType
 import androidx.health.connect.client.records.SpeedRecord
-import androidx.health.connect.client.records.StepsCadence
 import androidx.health.connect.client.records.StepsCadenceRecord
 import androidx.health.connect.client.records.StepsRecord
 import androidx.health.connect.client.records.SwimmingStrokesRecord
@@ -277,11 +274,11 @@
                 endZoneOffset = END_ZONE_OFFSET,
                 samples =
                     listOf(
-                        CyclingPedalingCadence(
+                        CyclingPedalingCadenceRecord.Sample(
                             time = START_TIME,
                             revolutionsPerMinute = 1.0,
                         ),
-                        CyclingPedalingCadence(
+                        CyclingPedalingCadenceRecord.Sample(
                             time = START_TIME,
                             revolutionsPerMinute = 2.0,
                         ),
@@ -303,15 +300,15 @@
                 endZoneOffset = END_ZONE_OFFSET,
                 samples =
                     listOf(
-                        HeartRate(
+                        HeartRateRecord.Sample(
                             time = START_TIME,
                             beatsPerMinute = 100L,
                         ),
-                        HeartRate(
+                        HeartRateRecord.Sample(
                             time = START_TIME,
                             beatsPerMinute = 110L,
                         ),
-                        HeartRate(
+                        HeartRateRecord.Sample(
                             time = START_TIME,
                             beatsPerMinute = 120L,
                         ),
@@ -637,11 +634,11 @@
                 endZoneOffset = END_ZONE_OFFSET,
                 samples =
                     listOf(
-                        StepsCadence(
+                        StepsCadenceRecord.Sample(
                             time = START_TIME,
                             rate = 1.0,
                         ),
-                        StepsCadence(
+                        StepsCadenceRecord.Sample(
                             time = START_TIME,
                             rate = 2.0,
                         ),
diff --git a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
index b295712..dd49758 100644
--- a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
@@ -21,6 +21,7 @@
 import androidx.health.platform.client.proto.BytesValue
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -36,6 +37,7 @@
         assertThat(parcelAndRead(protoParcelable).proto).isEqualTo(protoParcelable.proto)
     }
 
+    @Ignore // b/238635208
     @Test
     fun storeInSharedMemory() {
         // Big enough that it will be stored in shared memory.
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index f6f7ca1..7153d0f 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,7 +25,7 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=8757744
+androidx.playground.snapshotBuildId=8818165
 androidx.playground.metalavaBuildId=8670428
 androidx.playground.dokkaBuildId=7472101
 androidx.studio.type=playground
diff --git a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
index 34f094d..88efeb2 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
+++ b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
@@ -64,6 +64,7 @@
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -1101,6 +1102,7 @@
         assertThat(output).isEqualTo(Color.WHITE);
     }
 
+    @Ignore // b/238635208
     @Test
     public void placeholderIcon() {
         mComplicationRenderer.setComplicationData(
@@ -1112,6 +1114,7 @@
         assertThat(mComplicationRenderer.mIsPlaceholderIcon).isTrue();
     }
 
+    @Ignore // b/238635208
     @Test
     public void placeholderSmallImage() {
         mComplicationRenderer.setComplicationData(
@@ -1123,6 +1126,7 @@
         assertThat(mComplicationRenderer.mIsPlaceholderSmallImage).isTrue();
     }
 
+    @Ignore // b/238635208
     @Test
     public void placeholderPhotoImage() {
         mComplicationRenderer.setComplicationData(
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
index be20dbb..0d1a4e8 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
@@ -29,6 +29,7 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Assert.assertThrows
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -900,6 +901,7 @@
         assertThat(digestHash1).isNotEqualTo(digestHash2)
     }
 
+    @Ignore // b/238635208
     @Test
     public fun digestHashSensitiveToIconChanges() {
         val colorStyleSetting1 = ListUserStyleSetting(
diff --git a/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java b/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
index 7648800..c31650f 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
@@ -20,13 +20,12 @@
 import android.view.KeyEvent;
 import android.view.animation.Animation;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 import androidx.annotation.UiThread;
 import androidx.wear.utils.ActivityAnimationUtil;
 
-import org.jetbrains.annotations.NotNull;
-
 /**
  * Controller that handles the back button click for dismiss the frame layout
  *
@@ -49,7 +48,7 @@
                         && dismiss());
     }
 
-    void disable(@NotNull DismissibleFrameLayout layout) {
+    void disable(@NonNull DismissibleFrameLayout layout) {
         setOnDismissListener(null);
         layout.setOnKeyListener(null);
         // setting this to false will also ensure that this view is not focusable in touch mode
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
index f657cc2..23d44ca 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
@@ -167,4 +167,22 @@
             Assert.assertEquals("androidx.webkit.test", headerValue);
         }
     }
+
+    @Test
+    public void testEnterpriseAuthenticationAppLinkPolicyEnabled() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY);
+
+        assertTrue(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+                mWebViewOnUiThread.getSettings()));
+
+        WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(
+                mWebViewOnUiThread.getSettings(), false);
+        assertFalse(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+                mWebViewOnUiThread.getSettings()));
+
+        WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(
+                mWebViewOnUiThread.getSettings(), true);
+        assertTrue(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+                mWebViewOnUiThread.getSettings()));
+    }
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 477c312e..9e97fc8 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -724,6 +724,65 @@
         }
     }
 
+    /**
+     * Sets whether EnterpriseAuthenticationAppLinkPolicy if set by admin is allowed to have any
+     * effect on WebView.
+     * 

+ * EnterpriseAuthenticationAppLinkPolicy in WebView allows admins to specify authentication + * urls. When WebView is redirected to authentication url, and an app on the device has + * registered as the default handler for the url, that app is launched. + *

+ * EnterpriseAuthenticationAppLinkPolicy is enabled by default. + * + *

+ * This method should only be called if + * {@link WebViewFeature#isFeatureSupported(String)} + * returns true for {@link WebViewFeature#ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY}. + * + * @param enabled Whether EnterpriseAuthenticationAppLinkPolicy should be enabled. + * @hide + */ + @RestrictTo(RestrictTo.Scope.LIBRARY) + @RequiresFeature(name = WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, + enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported") + public static void setEnterpriseAuthenticationAppLinkPolicyEnabled( + @NonNull WebSettings settings, + boolean enabled) { + ApiFeature.NoFramework feature = + WebViewFeatureInternal.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY; + if (feature.isSupportedByWebView()) { + getAdapter(settings).setEnterpriseAuthenticationAppLinkPolicyEnabled(enabled); + } else { + throw WebViewFeatureInternal.getUnsupportedOperationException(); + } + } + + /** + * Gets whether EnterpriseAuthenticationAppLinkPolicy is allowed to have any effect on WebView. + * + *

+ * This method should only be called if + * {@link WebViewFeature#isFeatureSupported(String)} + * returns true for {@link WebViewFeature#ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY}. + * + * @return {@code true} if EnterpriseAuthenticationAppLinkPolicy is enabled and {@code false} + * otherwise. + * @hide + */ + @RestrictTo(RestrictTo.Scope.LIBRARY) + @RequiresFeature(name = WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, + enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported") + public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled( + @NonNull WebSettings settings) { + ApiFeature.NoFramework feature = + WebViewFeatureInternal.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY; + if (feature.isSupportedByWebView()) { + return getAdapter(settings).getEnterpriseAuthenticationAppLinkPolicyEnabled(); + } else { + throw WebViewFeatureInternal.getUnsupportedOperationException(); + } + } + private static WebSettingsAdapter getAdapter(WebSettings settings) { return WebViewGlueCommunicator.getCompatConverter().convertSettings(settings); }

diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 5de5148..8d50e6c 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -98,6 +98,7 @@
             GET_VARIATIONS_HEADER,
             ALGORITHMIC_DARKENING,
             REQUESTED_WITH_HEADER_CONTROL,
+            ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
     })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -492,6 +493,18 @@
     public static final String REQUESTED_WITH_HEADER_CONTROL = "REQUESTED_WITH_HEADER_CONTROL";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)}and
+     * {@link WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings)}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
+            "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+
+    /**
      * Return whether a feature is supported at run-time. On devices running Android version {@link
      * android.os.Build.VERSION_CODES#LOLLIPOP} and higher, this will check whether a feature is
      * supported, depending on the combination of the desired feature, the Android version of
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
index 963fb99..8d66c85 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
@@ -140,6 +140,22 @@
 
     /**
      * Adapter method for
+     * {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled}.
+     */
+    public void setEnterpriseAuthenticationAppLinkPolicyEnabled(boolean enabled) {
+        mBoundaryInterface.setEnterpriseAuthenticationAppLinkPolicyEnabled(enabled);
+    }
+
+    /**
+     * Adapter method for
+     * {@link androidx.webkit.WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled}.
+     */
+    public boolean getEnterpriseAuthenticationAppLinkPolicyEnabled() {
+        return mBoundaryInterface.getEnterpriseAuthenticationAppLinkPolicyEnabled();
+    }
+
+    /**
+     * Adapter method for
      * {@link androidx.webkit.WebSettingsCompat#getRequestedWithHeaderMode(android.webkit.WebSettings)}
      */
     public int getRequestedWithHeaderMode() {
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index fc179e8..aa106c7 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -473,6 +473,15 @@
             new ApiFeature.NoFramework(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
                     Features.REQUESTED_WITH_HEADER_CONTROL);
 
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)} and
+     * {@link androidx.webkit.WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings)}.
+     */
+    public static final ApiFeature.NoFramework ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
+            new ApiFeature.NoFramework(WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
+                    Features.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY);
     // --- Add new feature constants above this line ---
 
     private WebViewFeatureInternal() {
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
index c90bcc8..ea3ad1f 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
 import androidx.work.WorkInfo.State
 import androidx.work.WorkManager.UpdateResult.APPLIED_FOR_NEXT_RUN
 import androidx.work.WorkManager.UpdateResult.APPLIED_IMMEDIATELY
@@ -35,6 +36,8 @@
 import androidx.work.impl.testutils.TestConstraintTracker
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
 import androidx.work.impl.utils.taskexecutor.WorkManagerTaskExecutor
+import androidx.work.impl.workers.ARGUMENT_CLASS_NAME
+import androidx.work.impl.workers.ConstraintTrackingWorker
 import androidx.work.worker.LatchWorker
 import androidx.work.worker.RetryWorker
 import androidx.work.worker.TestWorker
@@ -413,8 +416,10 @@
         val spec = workManager.workDatabase.workSpecDao().getWorkSpec(request.stringId)!!
         val delta = spec.calculateNextRunTime() - System.currentTimeMillis()
         assertThat(delta).isGreaterThan(0)
-        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(request.stringId,
-            spec.lastEnqueueTime - delta)
+        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+            request.stringId,
+            spec.lastEnqueueTime - delta
+        )
         val updated = OneTimeWorkRequest.Builder(TestWorker::class.java).setId(request.id)
             .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.DAYS)
             .build()
@@ -431,8 +436,10 @@
             .setInitialDelay(10, TimeUnit.MINUTES).build()
         val enqueueTime = System.currentTimeMillis()
         workManager.enqueue(request).result.get()
-        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(request.stringId,
-            enqueueTime - TimeUnit.MINUTES.toMillis(5))
+        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+            request.stringId,
+            enqueueTime - TimeUnit.MINUTES.toMillis(5)
+        )
         val updated = OneTimeWorkRequest.Builder(TestWorker::class.java)
             .setInitialDelay(20, TimeUnit.MINUTES)
             .setId(request.id)
@@ -446,6 +453,24 @@
         assertThat(delta).isLessThan(TimeUnit.MINUTES.toMillis(16))
     }
 
+    @Test
+    @MediumTest
+    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
+    fun testUpdatePeriodicWorker_preservesConstraintTrackingWorker() {
+        val originRequest = OneTimeWorkRequest.Builder(TestWorker::class.java)
+            .setInitialDelay(10, TimeUnit.HOURS).build()
+        workManager.enqueue(originRequest).result.get()
+        val updateRequest = OneTimeWorkRequest.Builder(RetryWorker::class.java)
+            .setId(originRequest.id).setInitialDelay(10, TimeUnit.HOURS)
+            .setConstraints(Constraints(requiresBatteryNotLow = true))
+            .build()
+        workManager.updateWork(updateRequest).get()
+        val workSpec = db.workSpecDao().getWorkSpec(originRequest.stringId)!!
+        assertThat(workSpec.workerClassName).isEqualTo(ConstraintTrackingWorker::class.java.name)
+        assertThat(workSpec.input.getString(ARGUMENT_CLASS_NAME))
+            .isEqualTo(RetryWorker::class.java.name)
+    }
+
     @After
     fun tearDown() {
         workManager.cancelAllWork()
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt b/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
index 236e52c..b4435ea 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
@@ -23,6 +23,7 @@
 import androidx.work.WorkRequest
 import androidx.work.impl.model.WorkSpec
 import androidx.work.impl.utils.futures.SettableFuture
+import androidx.work.impl.utils.wrapInConstraintTrackingWorkerIfNeeded
 import com.google.common.util.concurrent.ListenableFuture
 
 private fun updateWorkImpl(
@@ -57,7 +58,8 @@
             runAttemptCount = oldWorkSpec.runAttemptCount,
             lastEnqueueTime = oldWorkSpec.lastEnqueueTime,
         )
-        workSpecDao.updateWorkSpec(updatedSpec)
+
+        workSpecDao.updateWorkSpec(wrapInConstraintTrackingWorkerIfNeeded(schedulers, updatedSpec))
         workTagDao.deleteByWorkSpecId(workSpecId)
         workTagDao.insertTags(workRequest)
         if (!isEnqueued) {
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
index cbd798f..5408bb6 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
@@ -25,24 +25,20 @@
 import static androidx.work.WorkInfo.State.FAILED;
 import static androidx.work.WorkInfo.State.RUNNING;
 import static androidx.work.WorkInfo.State.SUCCEEDED;
-import static androidx.work.impl.workers.ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME;
+import static androidx.work.impl.utils.EnqueueUtilsKt.wrapInConstraintTrackingWorkerIfNeeded;
 
 import android.content.Context;
-import android.os.Build;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
-import androidx.work.Constraints;
-import androidx.work.Data;
 import androidx.work.ExistingWorkPolicy;
 import androidx.work.Logger;
 import androidx.work.Operation;
 import androidx.work.WorkInfo;
 import androidx.work.WorkRequest;
 import androidx.work.impl.OperationImpl;
-import androidx.work.impl.Scheduler;
 import androidx.work.impl.Schedulers;
 import androidx.work.impl.WorkContinuationImpl;
 import androidx.work.impl.WorkDatabase;
@@ -53,7 +49,6 @@
 import androidx.work.impl.model.WorkName;
 import androidx.work.impl.model.WorkSpec;
 import androidx.work.impl.model.WorkSpecDao;
-import androidx.work.impl.workers.ConstraintTrackingWorker;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -306,20 +301,17 @@
                 workSpec.lastEnqueueTime = currentTimeMillis;
             }
 
-            if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
-                    && Build.VERSION.SDK_INT <= 25) {
-                tryDelegateConstrainedWorkSpec(workSpec);
-            } else if (Build.VERSION.SDK_INT <= WorkManagerImpl.MAX_PRE_JOB_SCHEDULER_API_LEVEL
-                    && usesScheduler(workManagerImpl, Schedulers.GCM_SCHEDULER)) {
-                tryDelegateConstrainedWorkSpec(workSpec);
-            }
-
             // If we have one WorkSpec with an enqueued state, then we need to schedule.
             if (workSpec.state == ENQUEUED) {
                 needsScheduling = true;
             }
 
-            workDatabase.workSpecDao().insertWorkSpec(workSpec);
+            workDatabase.workSpecDao().insertWorkSpec(
+                    wrapInConstraintTrackingWorkerIfNeeded(
+                            workManagerImpl.getSchedulers(),
+                            workSpec
+                    )
+            );
 
             if (hasPrerequisite) {
                 for (String prerequisiteId : prerequisiteIds) {
@@ -335,50 +327,4 @@
         }
         return needsScheduling;
     }
-
-    private static void tryDelegateConstrainedWorkSpec(WorkSpec workSpec) {
-        // requiresBatteryNotLow and requiresStorageNotLow require API 26 for JobScheduler.
-        // Delegate to ConstraintTrackingWorker between API 23-25.
-        Constraints constraints = workSpec.constraints;
-        String workerClassName = workSpec.workerClassName;
-        // Check if the Worker is a ConstraintTrackingWorker already. Otherwise we could end up
-        // wrapping a ConstraintTrackingWorker with another and build a taller stack.
-        // This usually happens when a developer accidentally enqueues() a named WorkRequest
-        // with an ExistingWorkPolicy.KEEP and subsequent inserts no-op (while the state of the
-        // Worker is not ENQUEUED or RUNNING i.e. the Worker probably just got done & the app is
-        // holding on to a reference of WorkSpec which got updated). We end up reusing the
-        // WorkSpec, and get a ConstraintTrackingWorker (instead of the original Worker class).
-        boolean isConstraintTrackingWorker =
-                workerClassName.equals(ConstraintTrackingWorker.class.getName());
-        if (!isConstraintTrackingWorker
-                && (constraints.requiresBatteryNotLow() || constraints.requiresStorageNotLow())) {
-            Data.Builder builder = new Data.Builder();
-            // Copy all arguments
-            builder.putAll(workSpec.input)
-                    .putString(ARGUMENT_CLASS_NAME, workerClassName);
-            workSpec.workerClassName = ConstraintTrackingWorker.class.getName();
-            workSpec.input = builder.build();
-        }
-    }
-
-    /**
-     * @param className The fully qualified class name of the {@link Scheduler}
-     * @return {@code true} if the {@link Scheduler} class is being used by WorkManager.
-     */
-    private static boolean usesScheduler(
-            @NonNull WorkManagerImpl workManager,
-            @NonNull String className) {
-
-        try {
-            Class klass = Class.forName(className);
-            for (Scheduler scheduler : workManager.getSchedulers()) {
-                if (klass.isAssignableFrom(scheduler.getClass())) {
-                    return true;
-                }
-            }
-            return false;
-        } catch (ClassNotFoundException ignore) {
-            return false;
-        }
-    }
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
new file mode 100644
index 0000000..a6cc806
--- /dev/null
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work.impl.utils
+
+import android.os.Build
+import androidx.work.Data
+import androidx.work.impl.Scheduler
+import androidx.work.impl.Schedulers
+import androidx.work.impl.WorkManagerImpl.MAX_PRE_JOB_SCHEDULER_API_LEVEL
+import androidx.work.impl.WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
+import androidx.work.impl.model.WorkSpec
+import androidx.work.impl.workers.ARGUMENT_CLASS_NAME
+import androidx.work.impl.workers.ConstraintTrackingWorker
+
+internal fun tryDelegateConstrainedWorkSpec(workSpec: WorkSpec): WorkSpec {
+    // requiresBatteryNotLow and requiresStorageNotLow require API 26 for JobScheduler.
+    // Delegate to ConstraintTrackingWorker between API 23-25.
+    val constraints = workSpec.constraints
+    val workerClassName = workSpec.workerClassName
+    // Check if the Worker is a ConstraintTrackingWorker already. Otherwise we could end up
+    // wrapping a ConstraintTrackingWorker with another and build a taller stack.
+    // This usually happens when a developer accidentally enqueues() a named WorkRequest
+    // with an ExistingWorkPolicy.KEEP and subsequent inserts no-op (while the state of the
+    // Worker is not ENQUEUED or RUNNING i.e. the Worker probably just got done & the app is
+    // holding on to a reference of WorkSpec which got updated). We end up reusing the
+    // WorkSpec, and get a ConstraintTrackingWorker (instead of the original Worker class).
+    val isConstraintTrackingWorker = workerClassName == ConstraintTrackingWorker::class.java.name
+    if (!isConstraintTrackingWorker &&
+        (constraints.requiresBatteryNotLow() || constraints.requiresStorageNotLow())
+    ) {
+        val newInputData = Data.Builder().putAll(workSpec.input)
+            .putString(ARGUMENT_CLASS_NAME, workerClassName)
+            .build()
+        return workSpec.copy(
+            input = newInputData,
+            workerClassName = ConstraintTrackingWorker::class.java.name
+        )
+    }
+    return workSpec
+}
+
+internal fun wrapInConstraintTrackingWorkerIfNeeded(
+    schedulers: List,
+    workSpec: WorkSpec,
+): WorkSpec {
+    return when {
+        Build.VERSION.SDK_INT in MIN_JOB_SCHEDULER_API_LEVEL..25 ->
+            tryDelegateConstrainedWorkSpec(workSpec)
+        Build.VERSION.SDK_INT <= MAX_PRE_JOB_SCHEDULER_API_LEVEL &&
+            usesScheduler(schedulers, Schedulers.GCM_SCHEDULER) ->
+            tryDelegateConstrainedWorkSpec(workSpec)
+        else -> workSpec
+    }
+}
+
+private fun usesScheduler(
+    schedulers: List,
+    className: String
+): Boolean {
+    return try {
+        val klass = Class.forName(className)
+        return schedulers.any { scheduler -> klass.isAssignableFrom(scheduler.javaClass) }
+    } catch (ignore: ClassNotFoundException) {
+        false
+    }
+}